145 lines
4.0 KiB
TypeScript
145 lines
4.0 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
import {
|
|
defaultKnowledgeOntologySettings,
|
|
getKnowledgeOntologyDisplayDefinitions,
|
|
isRelationEndpointAllowed,
|
|
knowledgeOntologySettingsSchema,
|
|
normalizeEntityTypeAlias,
|
|
normalizeRelationTypeAlias
|
|
} from './knowledge-ontology'
|
|
|
|
describe('knowledge ontology contract', () => {
|
|
it('provides the version 1 controlled defaults and useful aliases', () => {
|
|
expect(defaultKnowledgeOntologySettings.version).toBe(1)
|
|
expect(
|
|
defaultKnowledgeOntologySettings.entityTypes.map(({ id }) => id)
|
|
).toEqual([
|
|
'PERSON',
|
|
'ORGANIZATION',
|
|
'EVENT',
|
|
'LOCATION',
|
|
'DOCUMENT',
|
|
'CONCEPT'
|
|
])
|
|
expect(normalizeEntityTypeAlias('people')).toBe('PERSON')
|
|
expect(normalizeEntityTypeAlias('人员')).toBe('PERSON')
|
|
expect(normalizeEntityTypeAlias('公司')).toBe('ORGANIZATION')
|
|
expect(normalizeEntityTypeAlias('uncontrolled legacy type')).toBe('CONCEPT')
|
|
expect(normalizeRelationTypeAlias('depends on')).toBe('DEPENDS_ON')
|
|
expect(normalizeRelationTypeAlias('依赖于')).toBe('DEPENDS_ON')
|
|
})
|
|
|
|
it('rejects noncanonical ids, collisions, missing fallback, and bad endpoints', () => {
|
|
const concept = {
|
|
id: 'CONCEPT',
|
|
name: { zh: '概念', en: 'Concept' },
|
|
aliases: ['topic']
|
|
}
|
|
expect(() =>
|
|
knowledgeOntologySettingsSchema.parse({
|
|
entityTypes: [concept, { ...concept, id: 'person' }],
|
|
relationTypes: []
|
|
})
|
|
).toThrow()
|
|
expect(() =>
|
|
knowledgeOntologySettingsSchema.parse({
|
|
entityTypes: [
|
|
concept,
|
|
{
|
|
id: 'PERSON',
|
|
name: { zh: '人物', en: 'Person' },
|
|
aliases: ['shared']
|
|
},
|
|
{
|
|
id: 'ORGANIZATION',
|
|
name: { zh: '组织', en: 'Organization' },
|
|
aliases: ['shared']
|
|
}
|
|
],
|
|
relationTypes: []
|
|
})
|
|
).toThrow()
|
|
expect(() =>
|
|
knowledgeOntologySettingsSchema.parse({
|
|
entityTypes: [
|
|
{
|
|
id: 'PERSON',
|
|
name: { zh: '人物', en: 'Person' },
|
|
aliases: []
|
|
}
|
|
],
|
|
relationTypes: []
|
|
})
|
|
).toThrow()
|
|
expect(() =>
|
|
knowledgeOntologySettingsSchema.parse({
|
|
entityTypes: [concept],
|
|
relationTypes: [
|
|
{
|
|
id: 'KNOWS',
|
|
name: { zh: '认识', en: 'Knows' },
|
|
aliases: [],
|
|
sourceTypes: ['MISSING']
|
|
}
|
|
]
|
|
})
|
|
).toThrow()
|
|
})
|
|
|
|
it('enforces optional relation endpoint constraints', () => {
|
|
const settings = knowledgeOntologySettingsSchema.parse({
|
|
entityTypes: [
|
|
{
|
|
id: 'CONCEPT',
|
|
name: { zh: '概念', en: 'Concept' },
|
|
aliases: []
|
|
},
|
|
{
|
|
id: 'PERSON',
|
|
name: { zh: '人物', en: 'Person' },
|
|
aliases: ['people']
|
|
},
|
|
{
|
|
id: 'ORGANIZATION',
|
|
name: { zh: '组织', en: 'Organization' },
|
|
aliases: ['company']
|
|
}
|
|
],
|
|
relationTypes: [
|
|
{
|
|
id: 'WORKS_FOR',
|
|
name: { zh: '任职于', en: 'Works for' },
|
|
aliases: ['works for'],
|
|
sourceTypes: ['PERSON'],
|
|
targetTypes: ['ORGANIZATION']
|
|
}
|
|
]
|
|
})
|
|
|
|
expect(isRelationEndpointAllowed('works for', 'people', 'company', settings)).toBe(
|
|
true
|
|
)
|
|
expect(
|
|
isRelationEndpointAllowed('WORKS_FOR', 'ORGANIZATION', 'PERSON', settings)
|
|
).toBe(false)
|
|
expect(isRelationEndpointAllowed('UNKNOWN', 'PERSON', 'ORGANIZATION', settings)).toBe(
|
|
false
|
|
)
|
|
})
|
|
|
|
it('returns localized, detached display data', () => {
|
|
const display = getKnowledgeOntologyDisplayDefinitions(
|
|
defaultKnowledgeOntologySettings,
|
|
'en'
|
|
)
|
|
expect(display.fallbackEntityType).toBe('CONCEPT')
|
|
expect(display.entityTypes.find(({ id }) => id === 'PERSON')?.label).toBe(
|
|
'Person'
|
|
)
|
|
display.entityTypes[0]?.aliases.push('local mutation')
|
|
expect(defaultKnowledgeOntologySettings.entityTypes[0]?.aliases).not.toContain(
|
|
'local mutation'
|
|
)
|
|
})
|
|
})
|