Skip to content

Commit 245f881

Browse files
committed
fix(v2): derive track selection from appliesTo field, not track flag
The deriveCategorySelection function was incorrectly using the track flag as the basis for track selection. When no categories had track=true, it fell back to including ALL categories in track selection, even if they only had appliesTo: ['observation']. This fix changes the logic to respect the appliesTo field as the authoritative source for track selection: - Track selection now includes only categories where appliesTo includes 'track' - For observation-only payloads (all categories with appliesTo: ['observation']), track selection is correctly empty - The track flag is now just a hint; appliesTo is the source of truth Changes: - Removed fallback logic that defaulted to all categories - Changed filter to check c.definition.appliesTo.includes('track') - Added comprehensive test coverage (6 test cases) Tests verify: - Observation-only categories are excluded from track selection - Track selection respects appliesTo field regardless of track flag - Mixed payloads handle both observation and track categories correctly
1 parent e7068e3 commit 245f881

File tree

2 files changed

+122
-8
lines changed

2 files changed

+122
-8
lines changed

src/services/comapeocatBuilder.ts

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -271,14 +271,11 @@ function normalizeTags(tags: any, categoryId: string): Record<string, unknown> {
271271

272272
function deriveCategorySelection(categories: MappedCategory[]) {
273273
const observation = categories.map((c) => c.id);
274-
const track = categories.filter((c) => c.track).map((c) => c.id);
275-
276-
// If no categories have track=true, default to including all categories in track selection
277-
// This allows observation-only configs but provides sensible default for track
278-
if (track.length === 0) {
279-
console.warn('No categories marked with track=true, defaulting all categories to track selection');
280-
return { observation, track: observation };
281-
}
274+
// Track selection should only include categories whose appliesTo includes 'track'
275+
// The track flag is just a hint, but appliesTo is the authoritative field
276+
const track = categories
277+
.filter((c) => c.definition.appliesTo.includes('track'))
278+
.map((c) => c.id);
282279

283280
return { observation, track };
284281
}

src/tests/unit/services/comapeocatBuilder.test.ts

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,123 @@ describe('sanitizePathComponent security', () => {
231231
});
232232
});
233233

234+
describe('deriveCategorySelection', () => {
235+
it('includes only categories with appliesTo: track in track selection', () => {
236+
const categories = [
237+
{
238+
id: 'obs-only',
239+
definition: { name: 'Observation Only', appliesTo: ['observation'], tags: {}, fields: [] },
240+
track: false,
241+
},
242+
{
243+
id: 'track-enabled',
244+
definition: { name: 'Track Enabled', appliesTo: ['observation', 'track'], tags: {}, fields: [] },
245+
track: false,
246+
},
247+
];
248+
249+
const selection = __test__.deriveCategorySelection(categories);
250+
251+
expect(selection.observation).toEqual(['obs-only', 'track-enabled']);
252+
expect(selection.track).toEqual(['track-enabled']);
253+
});
254+
255+
it('returns empty track selection when no categories have appliesTo: track', () => {
256+
const categories = [
257+
{
258+
id: 'obs-1',
259+
definition: { name: 'Observation 1', appliesTo: ['observation'], tags: {}, fields: [] },
260+
track: false,
261+
},
262+
{
263+
id: 'obs-2',
264+
definition: { name: 'Observation 2', appliesTo: ['observation'], tags: {}, fields: [] },
265+
track: false,
266+
},
267+
];
268+
269+
const selection = __test__.deriveCategorySelection(categories);
270+
271+
expect(selection.observation).toEqual(['obs-1', 'obs-2']);
272+
expect(selection.track).toEqual([]);
273+
});
274+
275+
it('includes category with track=true in track selection when appliesTo includes track', () => {
276+
const categories = [
277+
{
278+
id: 'track-cat',
279+
definition: { name: 'Track Category', appliesTo: ['observation', 'track'], tags: {}, fields: [] },
280+
track: true,
281+
},
282+
];
283+
284+
const selection = __test__.deriveCategorySelection(categories);
285+
286+
expect(selection.observation).toEqual(['track-cat']);
287+
expect(selection.track).toEqual(['track-cat']);
288+
});
289+
290+
it('does not include observation-only category in track selection even with track=true', () => {
291+
const categories = [
292+
{
293+
id: 'obs-only',
294+
definition: { name: 'Observation Only', appliesTo: ['observation'], tags: {}, fields: [] },
295+
track: true, // track flag should be ignored if appliesTo doesn't include 'track'
296+
},
297+
];
298+
299+
const selection = __test__.deriveCategorySelection(categories);
300+
301+
expect(selection.observation).toEqual(['obs-only']);
302+
expect(selection.track).toEqual([]);
303+
});
304+
305+
it('includes track-only category in track selection', () => {
306+
const categories = [
307+
{
308+
id: 'track-only',
309+
definition: { name: 'Track Only', appliesTo: ['track'], tags: {}, fields: [] },
310+
track: false,
311+
},
312+
];
313+
314+
const selection = __test__.deriveCategorySelection(categories);
315+
316+
expect(selection.observation).toEqual(['track-only']);
317+
expect(selection.track).toEqual(['track-only']);
318+
});
319+
320+
it('handles mixed categories correctly', () => {
321+
const categories = [
322+
{
323+
id: 'obs-1',
324+
definition: { name: 'Observation 1', appliesTo: ['observation'], tags: {}, fields: [] },
325+
track: false,
326+
},
327+
{
328+
id: 'both-1',
329+
definition: { name: 'Both 1', appliesTo: ['observation', 'track'], tags: {}, fields: [] },
330+
track: false,
331+
},
332+
{
333+
id: 'obs-2',
334+
definition: { name: 'Observation 2', appliesTo: ['observation'], tags: {}, fields: [] },
335+
track: true, // Should not affect track selection
336+
},
337+
{
338+
id: 'both-2',
339+
definition: { name: 'Both 2', appliesTo: ['track', 'observation'], tags: {}, fields: [] },
340+
track: true,
341+
},
342+
];
343+
344+
const selection = __test__.deriveCategorySelection(categories);
345+
346+
expect(selection.observation).toEqual(['obs-1', 'both-1', 'obs-2', 'both-2']);
347+
expect(selection.track).toEqual(['both-1', 'both-2']);
348+
});
349+
});
350+
234351
function createBasePayload(options?: { fieldTypeOverride?: string }): BuildRequestV2 {
235352
return {
236353
metadata: { name: 'test', version: '1.0.0' },

0 commit comments

Comments
 (0)