test(13-02): add failing SourceRegistry denylist-gate spec
RED — proves Erfolgskriterium 4 (INGEST-07): registering an adapter whose portals include vergabe24 or aumass must throw DeniedPortalError, including a mixed portals array with one denylisted entry. Also covers legitimate register/get/activeAdapters happy paths. Fake adapter stub, no real scraping. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import type { RawTenderRecord, SourceType } from './tender.types';
|
||||
import type { TenderSourceAdapter } from './adapters/tender-source-adapter.interface';
|
||||
import { DeniedPortalError, SourceRegistry } from './source-registry';
|
||||
|
||||
/**
|
||||
* Fake adapter stub — no real scraping/HTTP, just satisfies the
|
||||
* TenderSourceAdapter contract for registry-behavior tests.
|
||||
*/
|
||||
function fakeAdapter(
|
||||
sourceType: SourceType,
|
||||
portals: readonly string[],
|
||||
): TenderSourceAdapter {
|
||||
return {
|
||||
sourceType,
|
||||
portals,
|
||||
async fetchTenders(_dayCursor: string): Promise<RawTenderRecord[]> {
|
||||
return [];
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
describe('SourceRegistry', () => {
|
||||
it('throws DeniedPortalError when registering an adapter serving vergabe24', () => {
|
||||
const registry = new SourceRegistry();
|
||||
const adapter = fakeAdapter('ai-netserver', ['vergabe24']);
|
||||
|
||||
expect(() => registry.register(adapter)).toThrow(DeniedPortalError);
|
||||
});
|
||||
|
||||
it('throws DeniedPortalError when registering an adapter serving aumass', () => {
|
||||
const registry = new SourceRegistry();
|
||||
const adapter = fakeAdapter('cosinex-dtvp', ['aumass']);
|
||||
|
||||
expect(() => registry.register(adapter)).toThrow(DeniedPortalError);
|
||||
});
|
||||
|
||||
it('rejects a mixed portals array wholesale when one entry is denylisted', () => {
|
||||
const registry = new SourceRegistry();
|
||||
const adapter = fakeAdapter('ai-netserver', ['tender24', 'aumass']);
|
||||
|
||||
expect(() => registry.register(adapter)).toThrow(DeniedPortalError);
|
||||
expect(registry.get('ai-netserver')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('registers a legitimate adapter and returns it via get()', () => {
|
||||
const registry = new SourceRegistry();
|
||||
const adapter = fakeAdapter('ai-netserver', ['tender24']);
|
||||
|
||||
expect(() => registry.register(adapter)).not.toThrow();
|
||||
expect(registry.get('ai-netserver')).toBe(adapter);
|
||||
});
|
||||
|
||||
it('activeAdapters() returns all registered adapters', () => {
|
||||
const registry = new SourceRegistry();
|
||||
const doe = fakeAdapter('doe-opendata', ['doe-opendata']);
|
||||
const netserver = fakeAdapter('ai-netserver', ['tender24']);
|
||||
|
||||
registry.register(doe);
|
||||
registry.register(netserver);
|
||||
|
||||
expect(registry.activeAdapters()).toHaveLength(2);
|
||||
expect(registry.activeAdapters()).toEqual(
|
||||
expect.arrayContaining([doe, netserver]),
|
||||
);
|
||||
});
|
||||
|
||||
it('get() returns undefined for an unregistered sourceType (no throw)', () => {
|
||||
const registry = new SourceRegistry();
|
||||
|
||||
expect(registry.get('cosinex-dtvp')).toBeUndefined();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user