AI Implementation feature(901): Admin Area Challenges: List, Import/Export and Add/Edit Modal 1.01 (#42)
This commit was merged in pull request #42.
This commit is contained in:
@@ -12,8 +12,10 @@ import {
|
||||
buildInitialFiles,
|
||||
defaultMinimumPoints as pureDefault,
|
||||
deriveMinimumPoints,
|
||||
firstErrorTab,
|
||||
isValidIpOrHostname,
|
||||
mapServerFieldErrors,
|
||||
partitionFilesBySize,
|
||||
prefillChallengeFormDefaults,
|
||||
toCreateBody,
|
||||
validateChallengeForm,
|
||||
@@ -216,4 +218,100 @@ describe('syncChallengeForm', () => {
|
||||
expect(form.controls.minimumPoints.value).toBe(50);
|
||||
expect(out.protocol).toBe('WEB');
|
||||
});
|
||||
});
|
||||
|
||||
class StubAdminService {
|
||||
stageChallengeFile = jest.fn(async (file: File) => ({
|
||||
token: `tok-${file.name}`,
|
||||
publicUrl: `/uploads/challenges/${file.name}`,
|
||||
originalFilename: file.name,
|
||||
mimeType: file.type || 'application/octet-stream',
|
||||
sizeBytes: file.size,
|
||||
}));
|
||||
discardChallengeFile = jest.fn(async () => undefined);
|
||||
createChallenge = jest.fn(async () => undefined);
|
||||
updateChallenge = jest.fn(async () => undefined);
|
||||
getChallengeDetail = jest.fn(async () => null);
|
||||
}
|
||||
|
||||
describe('challenge form modal: regression Job 901 (pure)', () => {
|
||||
it('firstErrorTab maps port errors to the connection tab', () => {
|
||||
expect(firstErrorTab({ port: 'Port is required for NC' })).toBe('connection');
|
||||
expect(firstErrorTab({ ipAddress: 'bad' })).toBe('connection');
|
||||
expect(firstErrorTab({ protocol: 'bad' })).toBe('connection');
|
||||
expect(firstErrorTab({ name: 'required' })).toBe('general');
|
||||
expect(firstErrorTab({ flag: 'required' })).toBe('general');
|
||||
expect(firstErrorTab({})).toBeNull();
|
||||
});
|
||||
|
||||
it('Case 3: blank NC port triggers validateChallengeForm port error', () => {
|
||||
const errs = validateChallengeForm(
|
||||
makeDefaults({
|
||||
protocol: 'NC',
|
||||
port: null,
|
||||
}),
|
||||
);
|
||||
expect(errs.port).toBe('Port is required for NC');
|
||||
});
|
||||
|
||||
it('Case 3: port FormControl has a pattern validator that rejects non-digits', () => {
|
||||
const form = buildChallengeFormGroup(new FormBuilder());
|
||||
form.controls.port.setValue('12a3' as unknown as number);
|
||||
form.controls.port.markAsTouched();
|
||||
expect(form.controls.port.errors?.['pattern']).toBeDefined();
|
||||
});
|
||||
|
||||
it('Case 4: validateChallengeForm rejects blank NC port regardless of blank IP', () => {
|
||||
const errs = validateChallengeForm(
|
||||
makeDefaults({
|
||||
protocol: 'NC',
|
||||
port: null,
|
||||
ipAddress: '',
|
||||
}),
|
||||
);
|
||||
expect(errs.port).toBe('Port is required for NC');
|
||||
expect(errs.ipAddress).toBeUndefined();
|
||||
});
|
||||
|
||||
it('Case 5: oversize file is rejected by partitionFilesBySize', () => {
|
||||
const small = new File([new Uint8Array(100)], 'ok.txt', { type: 'text/plain' });
|
||||
const big = new File([new Uint8Array(2048)], 'big.bin', { type: 'application/octet-stream' });
|
||||
const out = partitionFilesBySize([small, big], 1024);
|
||||
expect(out.accepted.length).toBe(1);
|
||||
expect(out.accepted[0].file).toBe(small);
|
||||
expect(out.rejected.length).toBe(1);
|
||||
expect(out.rejected[0].file).toBe(big);
|
||||
expect(out.rejected[0].reason).toContain('big.bin');
|
||||
expect(out.rejected[0].reason).toContain('global size limit');
|
||||
});
|
||||
|
||||
it('Case 6: drop-zone partitioning routes valid files to be staged', async () => {
|
||||
const stub = new StubAdminService();
|
||||
const small = new File([new Uint8Array(10)], 'dropped.txt', { type: 'text/plain' });
|
||||
const dt = { files: [small], dropEffect: 'copy' } as unknown as DataTransfer;
|
||||
const ev = { preventDefault: jest.fn(), dataTransfer: dt } as unknown as DragEvent;
|
||||
expect(ev.dataTransfer?.files.length).toBe(1);
|
||||
const outcome = partitionFilesBySize(
|
||||
Array.from(ev.dataTransfer!.files),
|
||||
50 * 1024 * 1024,
|
||||
);
|
||||
expect(outcome.accepted.length).toBe(1);
|
||||
expect(outcome.rejected.length).toBe(0);
|
||||
expect(outcome.accepted[0].file.name).toBe('dropped.txt');
|
||||
const staged = await stub.stageChallengeFile(outcome.accepted[0].file);
|
||||
expect(staged.token).toBe('tok-dropped.txt');
|
||||
});
|
||||
|
||||
it('Case 6: drop-zone partitioning rejects oversize files without network call', () => {
|
||||
const stub = new StubAdminService();
|
||||
const big = new File([new Uint8Array(2048)], 'big.bin', { type: 'application/octet-stream' });
|
||||
const dt = { files: [big], dropEffect: 'copy' } as unknown as DataTransfer;
|
||||
const outcome = partitionFilesBySize(
|
||||
Array.from(dt.files),
|
||||
1024,
|
||||
);
|
||||
expect(outcome.accepted.length).toBe(0);
|
||||
expect(outcome.rejected.length).toBe(1);
|
||||
expect(stub.stageChallengeFile).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user