Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ import { cardioSyntaxResultFixtures } from './modelResults/cardioSyntaxFixtures'
import { cathEfClipResultFixtures } from './modelResults/cathEfClipFixtures';
import { cathEfResultFixtures } from './modelResults/cathEfFixtures';
import { deepCoroClipResultFixtures } from './modelResults/deepCoroClipFixtures';
import { deepCoroCtoResultFixtures } from './modelResults/deepCoroCtoFixtures';
import { deepCoroMaceResultFixtures } from './modelResults/deepCoroMaceFixtures';
import { deepCoroSyntaxResultFixtures } from './modelResults/deepCoroSyntaxFixtures';
import { deepRVClipResultFixtures } from './modelResults/deepRVClipFixtures';
import { deepRVResultFixtures } from './modelResults/deepRVFixtures';

Expand Down Expand Up @@ -268,7 +270,7 @@ describe('ModelExecutionResultDrawer', () => {
test('selects the DeepCORO-CLIP renderer for an exact supported result', () => {
const result = {
...modelExecutionResultFixtures.available,
modelName: 'DeepCoro_CLIP_generic',
modelName: 'DeepCORO-CLIP',
modelVersion: '1.0.0',
result: deepCoroClipResultFixtures.validV1,
};
Expand Down Expand Up @@ -342,7 +344,7 @@ describe('ModelExecutionResultDrawer', () => {
test('selects the DeepCORO-MACE renderer for an exact supported result', () => {
const result = {
...modelExecutionResultFixtures.available,
modelName: 'DeepCORO_MACE',
modelName: 'DeepCORO-MACE',
modelVersion: '1.0.0',
result: deepCoroMaceResultFixtures.validV1,
};
Expand All @@ -365,6 +367,37 @@ describe('ModelExecutionResultDrawer', () => {
).toHaveLength(0);
});

test.each([
['DeepCORO-CTO', '2.0.0', deepCoroCtoResultFixtures.validV2, 'deepcoro-cto-result'],
['DeepCORO-SYNTAX', '5.0.0', deepCoroSyntaxResultFixtures.validV5, 'deepcoro-syntax-result'],
])(
'selects the %s renderer for an exact supported result',
(modelName, modelVersion, result, testId) => {
act(() => {
renderer = TestRenderer.create(
<ModelExecutionResultDrawer
state={queryState({
status: 'ready',
result: {
...modelExecutionResultFixtures.available,
modelName,
modelVersion,
result,
},
})}
onClose={jest.fn()}
onRetry={jest.fn()}
/>
);
});

expect(renderer!.root.findAllByProps({ 'data-testid': testId })).toHaveLength(1);
expect(
renderer!.root.findAllByProps({ 'data-testid': 'generic-model-result-collection' })
).toHaveLength(0);
}
);

test.each([
[modelExecutionResultFailureFixtures.notReady, 'viewable completed result'],
[modelExecutionResultFailureFixtures.terminalWithoutResult, 'missing'],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,19 @@ export * from './modelResults/cardioSyntaxContract';
export * from './modelResults/cathEfClipContract';
export * from './modelResults/cathEfContract';
export * from './modelResults/deepCoroClipContract';
export * from './modelResults/deepCoroCtoContract';
export * from './modelResults/deepCoroMaceContract';
export * from './modelResults/deepCoroSyntaxContract';
export * from './modelResults/deepRVClipContract';
export * from './modelResults/deepRVContract';
export * from './modelResults/modelResultRendererRegistry';
export * from './modelResults/CardioSyntaxResult';
export * from './modelResults/CathEfClipResult';
export * from './modelResults/CathEfResult';
export * from './modelResults/DeepCoroClipResult';
export * from './modelResults/DeepCoroCtoResult';
export * from './modelResults/DeepCoroMaceResult';
export * from './modelResults/DeepCoroSyntaxResult';
export * from './modelResults/DeepRVClipResult';
export * from './modelResults/DeepRVResult';
export * from './modelResults/ModelResultRenderer';
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Approved scope

- Priority model: `DeepCoro_CLIP_generic`
- Supported model identities: `DeepCoro_CLIP_generic` and deployed display identity `DeepCORO-CLIP`
- First supported execution model version: `1.0.0`
- Deployed image family: `heartwisehub/pacs-ai-deepcoro-clip-generic:1.0`
- Transport: the existing tenant-scoped execution-result endpoint from issues #392 and #393
Expand All @@ -13,7 +13,7 @@
Only aggregate model/version/status counts and JSON key names/types were inspected on 2026-08-16.
No patient identifiers, study identifiers, result values, or source payloads were copied into this
repository. The deployed service reports completed results under canonical model
`DeepCoro_CLIP_generic` and version `1.0.0`.
`DeepCoro_CLIP_generic` or `DeepCORO-CLIP`, and version `1.0.0`.

Every supported payload contains:

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# DeepCORO-CTO result presentation contract

The dedicated renderer is selected only for `DeepCORO-CTO` version `2.0.0`. It accepts the
deployed per-artery JSON output containing the imaging J-CTO score, selected artery, four
morphology components, and LAD/RCA/LCx scores.

Scores and probabilities must be finite and within their documented ranges. Model-authored
diagnosis, labels, warnings, and recommendation markup are not displayed. The UI uses fixed
component labels and fixed research/LCx cautions. Malformed or future payloads fall back to the
generic renderer.
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import React from 'react';
import TestRenderer, { act } from 'react-test-renderer';
import { DeepCoroCtoResult } from './DeepCoroCtoResult';
import { deepCoroCtoResultFixtures } from './deepCoroCtoFixtures';

jest.mock('react-i18next', () => ({
useTranslation: () => ({
t: (_key: string, options: Record<string, unknown> = {}) => String(options.defaultValue ?? ''),
i18n: { language: 'en-US' },
}),
}));

describe('DeepCoroCtoResult', () => {
test('presents score, components, and per-artery results', () => {
let renderer: TestRenderer.ReactTestRenderer;
act(() => {
renderer = TestRenderer.create(
<DeepCoroCtoResult payload={deepCoroCtoResultFixtures.parsedV2} />
);
});
expect(renderer!.root.findAllByProps({ 'data-testid': 'deepcoro-cto-result' })).toHaveLength(1);
expect(renderer!.root.findAll(node => node.type === 'table')).toHaveLength(2);
const rendered = JSON.stringify(renderer!.toJSON());
expect(rendered).toContain('RCA');
expect(rendered).toContain('71.0%');
expect(rendered).toContain('difficult');
});

test('uses fixed safety text instead of model-authored strings', () => {
let renderer: TestRenderer.ReactTestRenderer;
act(() => {
renderer = TestRenderer.create(
<DeepCoroCtoResult payload={deepCoroCtoResultFixtures.parsedV2} />
);
});
const rendered = JSON.stringify(renderer!.toJSON());
expect(rendered).not.toContain('Synthetic J-CTO result');
expect(rendered).not.toContain('<strong>');
expect(renderer!.root.findAllByProps({ role: 'note' })).toHaveLength(1);
});

test('shows a fixed caution for a selected LCx result', () => {
let renderer: TestRenderer.ReactTestRenderer;
act(() => {
renderer = TestRenderer.create(
<DeepCoroCtoResult
payload={{
...deepCoroCtoResultFixtures.parsedV2,
predictions: { ...deepCoroCtoResultFixtures.parsedV2.predictions, ctoArtery: 'LCx' },
}}
/>
);
});
expect(
renderer!.root.findAllByProps({ 'data-testid': 'deepcoro-cto-lcx-warning' })
).toHaveLength(1);
});
});
Loading
Loading