Files
DECNET/decnet_web/src/components/AttackerDetail/sections/CommandsViewer.test.tsx
anti f2b3393669 chore: relicense to AGPL-3.0-or-later and add SPDX headers
Replaces LICENSE (GPLv3 -> AGPLv3) and prepends
`SPDX-License-Identifier: AGPL-3.0-or-later` to every source file
across decnet/, decnet_web/, tests/, scripts/, and tools/.

Rationale: closes the GPLv3 ASP loophole so any party operating a
modified DECNET as a network service must offer their modified
source. Personal copyright (Samuel Paschuan) + inbound=outbound
contributions make a future unilateral relicense infeasible.

- LICENSE: full AGPL-3.0 text (gnu.org/licenses/agpl-3.0.txt)
- COPYRIGHT: project copyright notice
- tools/add_spdx_headers.py: idempotent header injector
  (shebang- and PEP 263-aware)

Touches 1565 source files (.py, .ts, .tsx, .js, .jsx, .css, .sh).
No behavior change; comments only.
2026-05-22 21:04:16 -04:00

104 lines
2.8 KiB
TypeScript

// SPDX-License-Identifier: AGPL-3.0-or-later
import { describe, it, expect, vi } from 'vitest';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { CommandsViewer } from './CommandsViewer';
import type { CommandRow } from '../types';
const row = (overrides: Partial<CommandRow> = {}): CommandRow => ({
service: 'ssh',
decky: 'decoy-01',
command: 'whoami',
timestamp: '2026-05-09T11:00:00Z',
...overrides,
});
describe('CommandsViewer', () => {
it('renders the title with the unfiltered total when serviceFilter is null', () => {
render(
<CommandsViewer
commands={[row()]}
cmdTotal={5}
cmdPage={1}
cmdLimit={50}
setCmdPage={() => {}}
serviceFilter={null}
open={true}
onToggle={() => {}}
/>,
);
expect(screen.getByText(/COMMANDS \(5\)/)).toBeInTheDocument();
});
it('appends the filter to the title when serviceFilter is set', () => {
render(
<CommandsViewer
commands={[row()]}
cmdTotal={3}
cmdPage={1}
cmdLimit={50}
setCmdPage={() => {}}
serviceFilter="ssh"
open={true}
onToggle={() => {}}
/>,
);
expect(screen.getByText(/COMMANDS \(3 SSH\)/)).toBeInTheDocument();
});
it('shows the empty state when commands is []', () => {
render(
<CommandsViewer
commands={[]}
cmdTotal={0}
cmdPage={1}
cmdLimit={50}
setCmdPage={() => {}}
serviceFilter={null}
open={true}
onToggle={() => {}}
/>,
);
expect(screen.getByText('NO COMMANDS CAPTURED')).toBeInTheDocument();
});
it('hides pagination when total fits on one page', () => {
render(
<CommandsViewer
commands={[row()]}
cmdTotal={1}
cmdPage={1}
cmdLimit={50}
setCmdPage={() => {}}
serviceFilter={null}
open={true}
onToggle={() => {}}
/>,
);
expect(screen.queryByText(/Page 1 of/)).not.toBeInTheDocument();
});
it('paginates: prev/next buttons fire setCmdPage with the right delta', async () => {
const user = userEvent.setup();
const setCmdPage = vi.fn();
render(
<CommandsViewer
commands={[row()]}
cmdTotal={250}
cmdPage={3}
cmdLimit={50}
setCmdPage={setCmdPage}
serviceFilter={null}
open={true}
onToggle={() => {}}
/>,
);
expect(screen.getByText('Page 3 of 5')).toBeInTheDocument();
const buttons = screen.getAllByRole('button');
await user.click(buttons[0]); // prev
expect(setCmdPage).toHaveBeenLastCalledWith(2);
await user.click(buttons[1]); // next
expect(setCmdPage).toHaveBeenLastCalledWith(4);
});
});