1import { describe, expect, it } from 'vitest'2import { lowerStructurizr, parseStructurizr } from '../src/languages/structurizr'3import { cacheKey } from '../src/units/structurizr'4import { NORMALIZER_BUILD } from '../../../shared/svg/index.js'5import awsSource from './fixtures/structurizr/aws.structurizr?raw'6import bigbankSource from './fixtures/structurizr/bigbank.structurizr?raw'7import docsSource from './fixtures/structurizr/docs.structurizr?raw'8import gettingStartedSource from './fixtures/structurizr/gettingstarted.structurizr?raw'9import scriptSource from './fixtures/structurizr/script.structurizr?raw'1011const fixtures = {12 'aws.structurizr': awsSource,13 'bigbank.structurizr': bigbankSource,14 'docs.structurizr': docsSource,15 'gettingstarted.structurizr': gettingStartedSource,16 'script.structurizr': scriptSource,17}18const fixture = (name: keyof typeof fixtures) => fixtures[name]1920describe('Structurizr cache namespace', () => {21 it('names the normalizer build', async () => {22 const key = await cacheKey({ source: 'workspace "A" {}', options: {}, metadata: { title: '', description: '' }, presentation: { background: '', padding: 0, frame: false } }, 'unit-test')23 expect(new URL(key.url).pathname).toContain(`/${NORMALIZER_BUILD}/unit-test/`)24 })25})2627describe('bounded Structurizr lowering', () => {28 it('covers the gettingstarted and bigbank view families', () => {29 const gettingStarted = parseStructurizr(fixture('gettingstarted.structurizr'))30 expect(gettingStarted.views).toHaveLength(1)31 expect(lowerStructurizr(gettingStarted, {})).toContain('@startuml')3233 const bigbank = parseStructurizr(fixture('bigbank.structurizr'))34 expect(bigbank.views.map(view => view.key)).toEqual(expect.arrayContaining([35 'SystemLandscape', 'SystemContext', 'Containers', 'Components', 'SignIn', 'DevelopmentDeployment', 'LiveDeployment',36 ]))37 expect(lowerStructurizr(bigbank, { 'view-key': 'Components' })).toContain('s_signinController')38 expect(lowerStructurizr(bigbank, { 'view-key': 'Components' })).toContain('Sign In Controller')39 expect(lowerStructurizr(bigbank, { 'view-key': 'SystemContext' })).not.toContain('s_signinController')40 expect(lowerStructurizr(bigbank, { 'view-key': 'Containers' })).not.toContain('s_signinController')41 expect(lowerStructurizr(bigbank, { 'view-key': 'Containers' })).toContain('s_singlePageApplication')42 const development = lowerStructurizr(bigbank, { 'view-key': 'DevelopmentDeployment' })43 expect(development).not.toContain('Failover')44 expect(lowerStructurizr(bigbank, { 'view-key': 'LiveDeployment' })).toContain('Failover')45 expect(lowerStructurizr(bigbank, { output: 'legend' })).toContain('endlegend')46 })4748 it('preserves deployment ownership and rejects unsafe directives/themes', () => {49 const aws = parseStructurizr(fixture('aws.structurizr'))50 expect(aws.elements.find(element => element.id === 'route53')?.parent).toBe('region')51 expect(() => parseStructurizr(fixture('script.structurizr'))).toThrow(/directive.*!script/i)52 expect(() => parseStructurizr(fixture('docs.structurizr'))).toThrow(/directive.*!docs/i)53 expect(() => parseStructurizr(`workspace "x" {54model {55 a = softwareSystem "A"56}57views {58 systemLandscape {59 include *60 }61 theme https://evil.example/theme.json62}63}`)).toThrow(/theme/i)64 })6566 it('does not interpolate hostile tags into PlantUML stereotypes', () => {67 const source = `workspace "x" {68model {69 a = softwareSystem "A" "" "" "bad>>tag"70}71views {72 systemLandscape {73 include *74 }75}76}`77 const output = lowerStructurizr(parseStructurizr(source), {})78 expect(output).not.toContain('bad>>tag')79 expect(output).not.toContain('<<bad')80 expect(() => parseStructurizr(source.replace('views {', 'styles { element "Person" { color #fff; } }\nviews {'))).toThrow()81 })82})