content: publish evidence-led portfolio copy

This commit is contained in:
2026-09-04 20:22:45 +03:00
parent 528099c74e
commit 38fb9e4330
8 changed files with 236 additions and 85 deletions

View File

@@ -37,6 +37,12 @@ const infrastructure: InfrastructureCaseStudy = {
summary: "Routine maintenance and investigation.",
},
],
featuredOperation: {
title: "Synthetic stateful service migration",
context: "A representative migration preserved application state.",
actions: ["Prepared versions.", "Created recovery checkpoints.", "Validated the new service."],
outcome: "The synthetic migration retained expected data.",
},
};
const privateProject: PublicProject = {
@@ -159,6 +165,7 @@ describe("semantic section components", () => {
it("renders a visible mailto contact and safe verified social links", () => {
const contact: ContactContent = {
introduction: "Open to System Administrator and Full-Stack Developer opportunities.",
email: "hello@example.test",
cvMessage:
"For a current CV or role-specific information, contact me directly.",

View File

@@ -15,11 +15,23 @@ const validContent = fixture('portfolio-content.valid.json');
const validManifest = fixture('asset-manifest.valid.json');
const validSite = fixture('site-config.preview.json');
type MutableContentFixture = {
identity: {
location: { city: string; country: string; countryCode: string };
availability: string;
};
navigation: Array<{ id: string }>;
profile: { introduction: string };
contact: { social: Array<{ service: string; url: string }> };
contact: { introduction: string; social: Array<{ service: string; url: string }> };
infrastructure: { featuredOperation: { actions: string[] } };
projects: {
selected: Array<{ visibility: string; url?: string; assetIds: string[]; title: string }>;
selected: Array<{
visibility: string;
url?: string;
assetIds: string[];
title: string;
media?: Array<{ assetId: string; alt: string; kind: 'image' }>;
mediaLayout?: 'single' | 'portrait-strip' | 'chart-pair';
}>;
additional: Array<{ assetIds: string[] }>;
};
toolkit: Array<{ technologies: Array<{ assetId?: string }> }>;
@@ -41,6 +53,25 @@ describe('content schemas', () => {
expect(parseSiteConfig(validSite).environment).toBe('preview');
});
it('requires public availability, city-level location, featured operations evidence, and contact copy', () => {
const content = cloneContent();
expect(content.identity.location).toEqual({ city: 'Bucharest', country: 'Romania', countryCode: 'RO' });
expect(content.identity.availability).toMatch(/Bucharest.*remote/iu);
expect(content.infrastructure.featuredOperation.actions.length).toBeGreaterThanOrEqual(3);
expect(content.contact.introduction).toMatch(/System Administrator.*Full-Stack Developer/iu);
delete (content.identity as Partial<typeof content.identity>).availability;
expect(() => parsePortfolioContent(content)).toThrow();
});
it('accepts only the approved project media layouts', () => {
const content = cloneContent();
content.projects.selected[0]!.mediaLayout = 'portrait-strip';
expect(() => parsePortfolioContent(content)).not.toThrow();
content.projects.selected[0]!.mediaLayout = 'carousel' as never;
expect(() => parsePortfolioContent(content)).toThrow(/mediaLayout|unrecognized/iu);
});
it('accepts an empty manifest before Phase 3 assets are prepared', () => {
expect(parsePublicAssetManifest({ assets: [] })).toEqual({ assets: [] });
});
@@ -117,6 +148,7 @@ describe('content schemas', () => {
const content = cloneContent();
content.projects.selected[0]!.title = 'Private self-hosted medication tracker';
for (const project of [...content.projects.selected, ...content.projects.additional]) project.assetIds = [];
delete content.projects.selected[0]!.media;
delete content.toolkit[0]!.technologies[0]!.assetId;
delete content.metadata.socialPreviewAssetId;
expect(() => validateProductionInputs(content, { assets: [] }, validSite, new URL('file:///prepared/'))).not.toThrow();
@@ -126,6 +158,7 @@ describe('content schemas', () => {
const content = cloneContent();
content.projects.selected[0]!.title = 'Private self-hosted medication tracker';
for (const project of [...content.projects.selected, ...content.projects.additional]) project.assetIds = [];
delete content.projects.selected[0]!.media;
content.toolkit[0]!.technologies[0]!.assetId = 'missing-toolkit-asset';
expect(() => validateProductionInputs(content, { assets: [] }, validSite, new URL('file:///prepared/')))
.toThrow(/unmanifested asset reference: missing-toolkit-asset/i);
@@ -205,6 +238,7 @@ describe('content schemas', () => {
it('rejects an asset reference absent from the manifest', () => {
const content = cloneContent();
content.projects.selected[0]!.assetIds = ['missing-asset'];
content.projects.selected[0]!.media![0]!.assetId = 'missing-asset';
expect(() => validateProductionInputs(content, validManifest, validSite, new URL('file:///prepared/')))
.toThrow(/unmanifested asset/i);
});
@@ -217,6 +251,7 @@ describe('content schemas', () => {
const unreferencedManifestAsset = cloneContent();
unreferencedManifestAsset.projects.selected[0]!.assetIds = [];
delete unreferencedManifestAsset.projects.selected[0]!.media;
expect(() => validateProductionInputs(unreferencedManifestAsset, validManifest, validSite, new URL('file:///prepared/')))
.toThrow(/unreferenced manifest asset/i);
});