chore: establish learning module baseline
This commit is contained in:
commit
2d04197f3f
2409 files changed
+533050
No files matched your search
@@ -0,0 +1,157 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import type { PPTImageElement } from '@openmaic/dsl';
|
||||
import type { MediaTask } from '@/lib/store/media-generation';
|
||||
import { resolveImageSrc } from '@/components/slide-renderer/components/element/ImageElement/useResolvedImageSrc';
|
||||
|
||||
const STAGE = 'stage-a';
|
||||
|
||||
const PLACEHOLDER: PPTImageElement = {
|
||||
id: 'el-placeholder',
|
||||
type: 'image',
|
||||
src: 'gen_img_alpha_001',
|
||||
left: 0,
|
||||
top: 0,
|
||||
width: 100,
|
||||
height: 100,
|
||||
rotate: 0,
|
||||
fixedRatio: false,
|
||||
};
|
||||
|
||||
const CONCRETE: PPTImageElement = {
|
||||
...PLACEHOLDER,
|
||||
id: 'el-concrete',
|
||||
src: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=',
|
||||
};
|
||||
|
||||
function task(over: Partial<MediaTask>): MediaTask {
|
||||
return {
|
||||
elementId: PLACEHOLDER.src,
|
||||
type: 'image',
|
||||
status: 'done',
|
||||
prompt: '',
|
||||
params: {} as MediaTask['params'],
|
||||
retryCount: 0,
|
||||
stageId: STAGE,
|
||||
...over,
|
||||
};
|
||||
}
|
||||
|
||||
describe('resolveImageSrc (pure)', () => {
|
||||
it('returns the objectUrl when the placeholder task is done', () => {
|
||||
const r = resolveImageSrc(
|
||||
PLACEHOLDER,
|
||||
STAGE,
|
||||
task({ status: 'done', objectUrl: 'blob:fake-1' }),
|
||||
);
|
||||
expect(r.resolvedSrc).toBe('blob:fake-1');
|
||||
expect(r.isPlaceholder).toBe(true);
|
||||
expect(r.task?.status).toBe('done');
|
||||
});
|
||||
|
||||
it('never returns a raw generated placeholder when no task is supplied', () => {
|
||||
const r = resolveImageSrc(PLACEHOLDER, STAGE, undefined);
|
||||
expect(r.resolvedSrc).toBe('');
|
||||
expect(r.resolution).toEqual({ kind: 'placeholder' });
|
||||
expect(r.isPlaceholder).toBe(true);
|
||||
expect(r.task).toBeUndefined();
|
||||
});
|
||||
|
||||
it.each(['pending', 'generating'] as const)('keeps task status %s non-renderable', (status) => {
|
||||
const r = resolveImageSrc(PLACEHOLDER, STAGE, task({ status, objectUrl: undefined }));
|
||||
expect(r.resolvedSrc).toBe('');
|
||||
expect(r.resolution).toEqual({ kind: 'pending' });
|
||||
expect(r.task?.status).toBe(status);
|
||||
});
|
||||
|
||||
it.each(['pending', 'generating'] as const)(
|
||||
'keeps a %s task authoritative over stale pool bytes',
|
||||
(status) => {
|
||||
const r = resolveImageSrc(
|
||||
PLACEHOLDER,
|
||||
STAGE,
|
||||
task({ status, objectUrl: undefined }),
|
||||
'blob:pool-image',
|
||||
);
|
||||
expect(r.resolvedSrc).toBe('');
|
||||
expect(r.resolution).toEqual({ kind: 'pending' });
|
||||
},
|
||||
);
|
||||
|
||||
it('renders last-good pool bytes for a failed regeneration', () => {
|
||||
const r = resolveImageSrc(
|
||||
PLACEHOLDER,
|
||||
STAGE,
|
||||
task({ status: 'failed', objectUrl: undefined }),
|
||||
'blob:pool-image',
|
||||
);
|
||||
expect(r.resolvedSrc).toBe('blob:pool-image');
|
||||
expect(r.resolution).toEqual({ kind: 'url', url: 'blob:pool-image', retryable: true });
|
||||
});
|
||||
|
||||
it('returns failed without bytes so the surface can render error UI', () => {
|
||||
const r = resolveImageSrc(PLACEHOLDER, STAGE, task({ status: 'failed', objectUrl: undefined }));
|
||||
expect(r.resolvedSrc).toBe('');
|
||||
expect(r.resolution).toEqual({ kind: 'failed', retryable: true });
|
||||
});
|
||||
|
||||
it('falls back when a done task has no objectUrl set', () => {
|
||||
const r = resolveImageSrc(PLACEHOLDER, STAGE, task({ status: 'done', objectUrl: undefined }));
|
||||
expect(r.resolvedSrc).toBe('');
|
||||
expect(r.resolution).toEqual({ kind: 'pending' });
|
||||
});
|
||||
|
||||
it('cross-stage isolation: drops a done task that belongs to a different stage', () => {
|
||||
const r = resolveImageSrc(
|
||||
PLACEHOLDER,
|
||||
STAGE,
|
||||
task({ status: 'done', objectUrl: 'blob:other-stage', stageId: 'stage-other' }),
|
||||
);
|
||||
expect(r.resolvedSrc).toBe('');
|
||||
expect(r.resolution).toEqual({ kind: 'placeholder' });
|
||||
expect(r.task).toBeUndefined();
|
||||
});
|
||||
|
||||
it('does not consider anything a placeholder when there is no stageId', () => {
|
||||
// Even if a "done" task is supplied, no stageId → skip placeholder logic entirely.
|
||||
const r = resolveImageSrc(
|
||||
PLACEHOLDER,
|
||||
undefined,
|
||||
task({ status: 'done', objectUrl: 'blob:leak' }),
|
||||
);
|
||||
expect(r.resolvedSrc).toBe('');
|
||||
expect(r.resolution).toEqual({ kind: 'placeholder' });
|
||||
expect(r.isPlaceholder).toBe(false);
|
||||
expect(r.task).toBeUndefined();
|
||||
});
|
||||
|
||||
it('passes through non-placeholder src unchanged (additive contract)', () => {
|
||||
// Even with a done task supplied — the regex gate rejects non-placeholder src.
|
||||
const r = resolveImageSrc(
|
||||
CONCRETE,
|
||||
STAGE,
|
||||
task({ status: 'done', objectUrl: 'blob:should-not-touch' }),
|
||||
);
|
||||
expect(r.resolvedSrc).toBe(CONCRETE.src);
|
||||
expect(r.isPlaceholder).toBe(false);
|
||||
expect(r.task).toBeUndefined();
|
||||
});
|
||||
|
||||
it('falls back to an unknown browser-resolvable string when pool lookup misses', () => {
|
||||
const unknown = { ...PLACEHOLDER, src: 'logo.png' };
|
||||
const r = resolveImageSrc(unknown, STAGE, undefined, null);
|
||||
expect(r.resolvedSrc).toBe('logo.png');
|
||||
});
|
||||
|
||||
it('hides an opaque allocated ref when pool lookup misses', () => {
|
||||
const allocated = { ...PLACEHOLDER, src: 'ast_missing_image' };
|
||||
const r = resolveImageSrc(allocated, STAGE, undefined, null);
|
||||
expect(r.resolvedSrc).toBe('');
|
||||
expect(r.resolution).toEqual({ kind: 'placeholder' });
|
||||
});
|
||||
|
||||
it('returns disabled for an unresolved placeholder when generation is off', () => {
|
||||
const r = resolveImageSrc(PLACEHOLDER, STAGE, undefined, null, true);
|
||||
expect(r.resolvedSrc).toBe('');
|
||||
expect(r.resolution).toEqual({ kind: 'disabled' });
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user