diff --git a/apps/web/app/lib/csv-export.test.ts b/apps/web/app/lib/csv-export.test.ts index 105c4778..2dd56383 100644 --- a/apps/web/app/lib/csv-export.test.ts +++ b/apps/web/app/lib/csv-export.test.ts @@ -335,6 +335,8 @@ describe('isUnfilteredCsvExport', () => { bidder: 'acme', q: 'rail', bids: 'one', + flags: ['no_competition'], + authorityTypes: ['министерство'], types: ['municipality'], kinds: ['company'], countBucket: '2-5', diff --git a/apps/web/app/lib/csv-export.ts b/apps/web/app/lib/csv-export.ts index 078dfc0c..2385a16f 100644 --- a/apps/web/app/lib/csv-export.ts +++ b/apps/web/app/lib/csv-export.ts @@ -5,7 +5,15 @@ const CSV_CONTENT_TYPE = 'text/csv; charset=utf-8'; const CSV_CACHE_CONTROL = 'public, max-age=3600'; const CSV_MULTIPART_PART_SIZE = 8 * 1024 * 1024; -const ARRAY_FILTERS = ['years', 'sectors', 'procedureGroups', 'kinds', 'types'] as const; +const ARRAY_FILTERS = [ + 'years', + 'sectors', + 'procedureGroups', + 'kinds', + 'types', + 'flags', + 'authorityTypes', +] as const; // `bids` ('one' | null) is a response-affecting filter: without it here a „само една оферта" export // was misclassified as unfiltered and served from / written to the shared unfiltered cache object — // a cache-poisoning variant of #56/#122 on top of the wrong-data bug (#138). hasScalarFilter treats diff --git a/apps/web/app/lib/filters.test.ts b/apps/web/app/lib/filters.test.ts index 158f1609..660900ac 100644 --- a/apps/web/app/lib/filters.test.ts +++ b/apps/web/app/lib/filters.test.ts @@ -1,5 +1,6 @@ import { describe, expect, it } from 'vitest'; import { CPV_SECTORS } from '@sigma/config'; +import { FLAG_TYPES } from '@sigma/db'; import { authorityListFilters, companyListFilters, @@ -13,9 +14,44 @@ import { withParams, } from './filters'; import { CANONICAL_QUERY_PARAMS } from './query-params'; +import type { RiskFlagType } from './riskLogic'; const sp = (q: string) => new URLSearchParams(q); +describe('contract risk-signal + authority-type filters (#218)', () => { + it('parses valid ?flag tokens and drops unknown ones', () => { + const f = contractListFilters(new URLSearchParams('flag=no_competition,all&flag=bogus')); + expect(f.flags).toEqual(['no_competition', 'all']); + }); + + it('leaves flags empty when the param is absent', () => { + expect(contractListFilters(new URLSearchParams('')).flags).toEqual([]); + }); + + it('parses ?type into authorityTypes', () => { + const f = contractListFilters(new URLSearchParams('type=министерство&type=община')); + expect(f.authorityTypes).toEqual(['министерство', 'община']); + }); + + it('drops unknown ?type buckets (cache-cardinality / DoS allow-list guard, #218 review)', () => { + const f = contractListFilters( + new URLSearchParams('type=министерство&type=