@elevasis/core 0.21.0 → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +2518 -2169
- package/dist/index.js +2495 -1095
- package/dist/knowledge/index.d.ts +706 -1044
- package/dist/knowledge/index.js +9 -9
- package/dist/organization-model/index.d.ts +2518 -2169
- package/dist/organization-model/index.js +2495 -1095
- package/dist/test-utils/index.d.ts +826 -1014
- package/dist/test-utils/index.js +1894 -1032
- package/package.json +3 -3
- package/src/__tests__/template-core-compatibility.test.ts +11 -79
- package/src/_gen/__tests__/__snapshots__/contracts.md.snap +852 -397
- package/src/auth/multi-tenancy/permissions.ts +20 -8
- package/src/business/README.md +2 -2
- package/src/business/acquisition/api-schemas.test.ts +175 -2
- package/src/business/acquisition/api-schemas.ts +132 -16
- package/src/business/acquisition/build-templates.test.ts +4 -4
- package/src/business/acquisition/build-templates.ts +72 -30
- package/src/business/acquisition/crm-state-actions.test.ts +13 -11
- package/src/business/acquisition/index.ts +12 -0
- package/src/business/acquisition/types.ts +7 -3
- package/src/business/clients/api-schemas.test.ts +115 -0
- package/src/business/clients/api-schemas.ts +158 -0
- package/src/business/clients/index.ts +1 -0
- package/src/business/deals/api-schemas.ts +8 -0
- package/src/business/index.ts +5 -2
- package/src/business/projects/types.ts +19 -0
- package/src/execution/engine/__tests__/fixtures/test-agents.ts +10 -8
- package/src/execution/engine/agent/core/__tests__/agent.test.ts +16 -12
- package/src/execution/engine/agent/core/__tests__/error-passthrough.test.ts +4 -3
- package/src/execution/engine/agent/core/types.ts +25 -15
- package/src/execution/engine/agent/index.ts +6 -4
- package/src/execution/engine/agent/reasoning/__tests__/request-builder.test.ts +24 -18
- package/src/execution/engine/index.ts +3 -0
- package/src/execution/engine/workflow/types.ts +9 -2
- package/src/knowledge/README.md +8 -7
- package/src/knowledge/__tests__/queries.test.ts +74 -73
- package/src/knowledge/format.ts +10 -9
- package/src/knowledge/index.ts +1 -1
- package/src/knowledge/published.ts +1 -1
- package/src/knowledge/queries.ts +26 -25
- package/src/organization-model/README.md +73 -26
- package/src/organization-model/__tests__/content-kinds-registry.test.ts +210 -0
- package/src/organization-model/__tests__/defaults.test.ts +76 -96
- package/src/organization-model/__tests__/domains/actions.test.ts +56 -0
- package/src/organization-model/__tests__/domains/customers.test.ts +299 -295
- package/src/organization-model/__tests__/domains/entities.test.ts +56 -0
- package/src/organization-model/__tests__/domains/goals.test.ts +493 -479
- package/src/organization-model/__tests__/domains/identity.test.ts +280 -279
- package/src/organization-model/__tests__/domains/navigation.test.ts +268 -212
- package/src/organization-model/__tests__/domains/offerings.test.ts +414 -419
- package/src/organization-model/__tests__/domains/policies.test.ts +323 -0
- package/src/organization-model/__tests__/domains/resource-mappings.test.ts +271 -271
- package/src/organization-model/__tests__/domains/resources.test.ts +310 -0
- package/src/organization-model/__tests__/domains/roles.test.ts +463 -347
- package/src/organization-model/__tests__/domains/statuses.test.ts +246 -243
- package/src/organization-model/__tests__/domains/systems.test.ts +209 -0
- package/src/organization-model/__tests__/flatten-additive-merge.test.ts +361 -0
- package/src/organization-model/__tests__/foundation.test.ts +74 -102
- package/src/organization-model/__tests__/get-resources-for-system.test.ts +144 -0
- package/src/organization-model/__tests__/graph.test.ts +899 -71
- package/src/organization-model/__tests__/knowledge.test.ts +209 -49
- package/src/organization-model/__tests__/lookup-helpers.test.ts +438 -0
- package/src/organization-model/__tests__/migration-helpers.test.ts +591 -0
- package/src/organization-model/__tests__/prospecting-ssot.test.ts +36 -27
- package/src/organization-model/__tests__/recursive-system-schema.test.ts +520 -0
- package/src/organization-model/__tests__/resolve.test.ts +174 -23
- package/src/organization-model/__tests__/schema.test.ts +291 -114
- package/src/organization-model/__tests__/surface-projection.test.ts +207 -97
- package/src/organization-model/catalogs/lead-gen.ts +144 -0
- package/src/organization-model/content-kinds/config.ts +36 -0
- package/src/organization-model/content-kinds/index.ts +74 -0
- package/src/organization-model/content-kinds/pipeline.ts +68 -0
- package/src/organization-model/content-kinds/registry.ts +44 -0
- package/src/organization-model/content-kinds/status.ts +71 -0
- package/src/organization-model/content-kinds/template.ts +83 -0
- package/src/organization-model/content-kinds/types.ts +117 -0
- package/src/organization-model/contracts.ts +13 -3
- package/src/organization-model/defaults.ts +499 -86
- package/src/organization-model/domains/actions.ts +239 -0
- package/src/organization-model/domains/customers.ts +78 -75
- package/src/organization-model/domains/entities.ts +144 -0
- package/src/organization-model/domains/goals.ts +83 -80
- package/src/organization-model/domains/knowledge.ts +76 -17
- package/src/organization-model/domains/navigation.ts +107 -384
- package/src/organization-model/domains/offerings.ts +71 -66
- package/src/organization-model/domains/policies.ts +102 -0
- package/src/organization-model/domains/projects.ts +14 -48
- package/src/organization-model/domains/prospecting.ts +62 -181
- package/src/organization-model/domains/resources.ts +145 -0
- package/src/organization-model/domains/roles.ts +96 -55
- package/src/organization-model/domains/sales.ts +10 -219
- package/src/organization-model/domains/shared.ts +57 -57
- package/src/organization-model/domains/statuses.ts +339 -130
- package/src/organization-model/domains/systems.ts +203 -0
- package/src/organization-model/foundation.ts +54 -67
- package/src/organization-model/graph/build.ts +682 -54
- package/src/organization-model/graph/link.ts +1 -1
- package/src/organization-model/graph/schema.ts +24 -9
- package/src/organization-model/graph/types.ts +20 -7
- package/src/organization-model/helpers.ts +231 -26
- package/src/organization-model/icons.ts +1 -0
- package/src/organization-model/index.ts +118 -5
- package/src/organization-model/migration-helpers.ts +249 -0
- package/src/organization-model/organization-graph.mdx +16 -15
- package/src/organization-model/organization-model.mdx +111 -44
- package/src/organization-model/published.ts +172 -19
- package/src/organization-model/resolve.ts +117 -54
- package/src/organization-model/schema.ts +654 -112
- package/src/organization-model/surface-projection.ts +116 -122
- package/src/organization-model/types.ts +146 -20
- package/src/platform/api/types.ts +38 -35
- package/src/platform/constants/versions.ts +1 -1
- package/src/platform/registry/__tests__/command-view.test.ts +6 -8
- package/src/platform/registry/__tests__/resource-link.test.ts +13 -8
- package/src/platform/registry/__tests__/resource-registry.integration.test.ts +16 -31
- package/src/platform/registry/__tests__/resource-registry.nested-systems.test.ts +245 -0
- package/src/platform/registry/__tests__/resource-registry.test.ts +2053 -2005
- package/src/platform/registry/__tests__/validation.test.ts +1347 -1086
- package/src/platform/registry/index.ts +14 -0
- package/src/platform/registry/resource-registry.ts +52 -2
- package/src/platform/registry/serialization.ts +241 -202
- package/src/platform/registry/serialized-types.ts +1 -0
- package/src/platform/registry/types.ts +411 -361
- package/src/platform/registry/validation.ts +745 -513
- package/src/projects/api-schemas.ts +290 -267
- package/src/reference/_generated/contracts.md +853 -397
- package/src/reference/glossary.md +23 -18
- package/src/supabase/database.types.ts +181 -0
- package/src/test-utils/test-utils.test.ts +1 -6
- package/src/organization-model/__tests__/domains/operations.test.ts +0 -203
- package/src/organization-model/domains/features.ts +0 -31
- package/src/organization-model/domains/operations.ts +0 -85
|
@@ -1,272 +1,272 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest'
|
|
2
|
-
import { ResourceMappingSchema, TechStackEntrySchema } from '../../domains/shared'
|
|
3
|
-
import { resolveOrganizationModel } from '../../resolve'
|
|
4
|
-
|
|
5
|
-
// ---------------------------------------------------------------------------
|
|
6
|
-
// Minimal valid resource mapping fixture (no techStack) for reuse.
|
|
7
|
-
// ---------------------------------------------------------------------------
|
|
8
|
-
|
|
9
|
-
const MINIMAL_MAPPING = {
|
|
10
|
-
id: 'rm-hubspot-sync',
|
|
11
|
-
label: 'HubSpot Sync',
|
|
12
|
-
resourceId: 'hubspot-sync-workflow',
|
|
13
|
-
resourceType: 'workflow' as const,
|
|
14
|
-
|
|
15
|
-
entityIds: [],
|
|
16
|
-
surfaceIds: [],
|
|
17
|
-
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
// ---------------------------------------------------------------------------
|
|
21
|
-
// Group 1: TechStackEntrySchema — positive parse
|
|
22
|
-
// ---------------------------------------------------------------------------
|
|
23
|
-
|
|
24
|
-
describe('TechStackEntrySchema — positive parse', () => {
|
|
25
|
-
it('accepts a fully-populated tech stack entry', () => {
|
|
26
|
-
const result = TechStackEntrySchema.safeParse({
|
|
27
|
-
platform: 'HubSpot',
|
|
28
|
-
purpose: 'Primary CRM and contact management',
|
|
29
|
-
credentialStatus: 'configured',
|
|
30
|
-
isSystemOfRecord: true
|
|
31
|
-
})
|
|
32
|
-
expect(result.success).toBe(true)
|
|
33
|
-
if (result.success) {
|
|
34
|
-
expect(result.data.platform).toBe('HubSpot')
|
|
35
|
-
expect(result.data.credentialStatus).toBe('configured')
|
|
36
|
-
expect(result.data.isSystemOfRecord).toBe(true)
|
|
37
|
-
}
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
it('isSystemOfRecord defaults to false when omitted', () => {
|
|
41
|
-
const result = TechStackEntrySchema.safeParse({
|
|
42
|
-
platform: 'Stripe',
|
|
43
|
-
purpose: 'Payment processing',
|
|
44
|
-
credentialStatus: 'pending'
|
|
45
|
-
})
|
|
46
|
-
expect(result.success).toBe(true)
|
|
47
|
-
if (result.success) {
|
|
48
|
-
expect(result.data.isSystemOfRecord).toBe(false)
|
|
49
|
-
}
|
|
50
|
-
})
|
|
51
|
-
|
|
52
|
-
it('accepts all four credentialStatus enum values', () => {
|
|
53
|
-
const statuses = ['configured', 'pending', 'expired', 'missing'] as const
|
|
54
|
-
for (const status of statuses) {
|
|
55
|
-
const result = TechStackEntrySchema.safeParse({
|
|
56
|
-
platform: 'SomePlatform',
|
|
57
|
-
purpose: 'Some purpose',
|
|
58
|
-
credentialStatus: status
|
|
59
|
-
})
|
|
60
|
-
expect(result.success).toBe(true)
|
|
61
|
-
}
|
|
62
|
-
})
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
// ---------------------------------------------------------------------------
|
|
66
|
-
// Group 2: TechStackEntrySchema — negative parse
|
|
67
|
-
// ---------------------------------------------------------------------------
|
|
68
|
-
|
|
69
|
-
describe('TechStackEntrySchema — negative parse', () => {
|
|
70
|
-
it('rejects invalid credentialStatus enum value', () => {
|
|
71
|
-
const result = TechStackEntrySchema.safeParse({
|
|
72
|
-
platform: 'HubSpot',
|
|
73
|
-
purpose: 'CRM',
|
|
74
|
-
credentialStatus: 'active'
|
|
75
|
-
})
|
|
76
|
-
expect(result.success).toBe(false)
|
|
77
|
-
})
|
|
78
|
-
|
|
79
|
-
it('rejects missing platform', () => {
|
|
80
|
-
const result = TechStackEntrySchema.safeParse({
|
|
81
|
-
purpose: 'CRM',
|
|
82
|
-
credentialStatus: 'configured'
|
|
83
|
-
})
|
|
84
|
-
expect(result.success).toBe(false)
|
|
85
|
-
})
|
|
86
|
-
|
|
87
|
-
it('rejects empty string platform', () => {
|
|
88
|
-
const result = TechStackEntrySchema.safeParse({
|
|
89
|
-
platform: '',
|
|
90
|
-
purpose: 'CRM',
|
|
91
|
-
credentialStatus: 'configured'
|
|
92
|
-
})
|
|
93
|
-
expect(result.success).toBe(false)
|
|
94
|
-
})
|
|
95
|
-
|
|
96
|
-
it('rejects missing purpose', () => {
|
|
97
|
-
const result = TechStackEntrySchema.safeParse({
|
|
98
|
-
platform: 'HubSpot',
|
|
99
|
-
credentialStatus: 'configured'
|
|
100
|
-
})
|
|
101
|
-
expect(result.success).toBe(false)
|
|
102
|
-
})
|
|
103
|
-
|
|
104
|
-
it('rejects empty string purpose', () => {
|
|
105
|
-
const result = TechStackEntrySchema.safeParse({
|
|
106
|
-
platform: 'HubSpot',
|
|
107
|
-
purpose: '',
|
|
108
|
-
credentialStatus: 'configured'
|
|
109
|
-
})
|
|
110
|
-
expect(result.success).toBe(false)
|
|
111
|
-
})
|
|
112
|
-
|
|
113
|
-
it('rejects missing credentialStatus', () => {
|
|
114
|
-
const result = TechStackEntrySchema.safeParse({
|
|
115
|
-
platform: 'HubSpot',
|
|
116
|
-
purpose: 'CRM'
|
|
117
|
-
})
|
|
118
|
-
expect(result.success).toBe(false)
|
|
119
|
-
})
|
|
120
|
-
})
|
|
121
|
-
|
|
122
|
-
// ---------------------------------------------------------------------------
|
|
123
|
-
// Group 3: ResourceMappingSchema — backward compatibility (no techStack)
|
|
124
|
-
// ---------------------------------------------------------------------------
|
|
125
|
-
|
|
126
|
-
describe('ResourceMappingSchema — backward compatibility without techStack', () => {
|
|
127
|
-
it('parses a mapping with no techStack key (legacy shape)', () => {
|
|
128
|
-
const result = ResourceMappingSchema.safeParse(MINIMAL_MAPPING)
|
|
129
|
-
expect(result.success).toBe(true)
|
|
130
|
-
if (result.success) {
|
|
131
|
-
expect(result.data.techStack).toBeUndefined()
|
|
132
|
-
}
|
|
133
|
-
})
|
|
134
|
-
|
|
135
|
-
it('parses a mapping with techStack explicitly set to undefined', () => {
|
|
136
|
-
const result = ResourceMappingSchema.safeParse({ ...MINIMAL_MAPPING, techStack: undefined })
|
|
137
|
-
expect(result.success).toBe(true)
|
|
138
|
-
if (result.success) {
|
|
139
|
-
expect(result.data.techStack).toBeUndefined()
|
|
140
|
-
}
|
|
141
|
-
})
|
|
142
|
-
|
|
143
|
-
it('all existing reference ID fields still default to empty arrays', () => {
|
|
144
|
-
const result = ResourceMappingSchema.safeParse({
|
|
145
|
-
id: 'rm-bare',
|
|
146
|
-
label: 'Bare mapping',
|
|
147
|
-
resourceId: 'bare-workflow',
|
|
148
|
-
resourceType: 'workflow'
|
|
149
|
-
})
|
|
150
|
-
expect(result.success).toBe(true)
|
|
151
|
-
if (result.success) {
|
|
152
|
-
expect(result.data.
|
|
153
|
-
expect(result.data.entityIds).toEqual([])
|
|
154
|
-
expect(result.data.surfaceIds).toEqual([])
|
|
155
|
-
expect(result.data.
|
|
156
|
-
expect(result.data.techStack).toBeUndefined()
|
|
157
|
-
}
|
|
158
|
-
})
|
|
159
|
-
})
|
|
160
|
-
|
|
161
|
-
// ---------------------------------------------------------------------------
|
|
162
|
-
// Group 4: ResourceMappingSchema — with techStack
|
|
163
|
-
// ---------------------------------------------------------------------------
|
|
164
|
-
|
|
165
|
-
describe('ResourceMappingSchema — with techStack', () => {
|
|
166
|
-
it('parses a mapping with a full techStack object', () => {
|
|
167
|
-
const result = ResourceMappingSchema.safeParse({
|
|
168
|
-
...MINIMAL_MAPPING,
|
|
169
|
-
techStack: {
|
|
170
|
-
platform: 'HubSpot',
|
|
171
|
-
purpose: 'Contact and deal management',
|
|
172
|
-
credentialStatus: 'configured',
|
|
173
|
-
isSystemOfRecord: true
|
|
174
|
-
}
|
|
175
|
-
})
|
|
176
|
-
expect(result.success).toBe(true)
|
|
177
|
-
if (result.success) {
|
|
178
|
-
expect(result.data.techStack).toBeDefined()
|
|
179
|
-
expect(result.data.techStack?.platform).toBe('HubSpot')
|
|
180
|
-
expect(result.data.techStack?.credentialStatus).toBe('configured')
|
|
181
|
-
expect(result.data.techStack?.isSystemOfRecord).toBe(true)
|
|
182
|
-
}
|
|
183
|
-
})
|
|
184
|
-
|
|
185
|
-
it('isSystemOfRecord defaults to false inside techStack when omitted', () => {
|
|
186
|
-
const result = ResourceMappingSchema.safeParse({
|
|
187
|
-
...MINIMAL_MAPPING,
|
|
188
|
-
techStack: {
|
|
189
|
-
platform: 'Notion',
|
|
190
|
-
purpose: 'Internal knowledge base',
|
|
191
|
-
credentialStatus: 'pending'
|
|
192
|
-
}
|
|
193
|
-
})
|
|
194
|
-
expect(result.success).toBe(true)
|
|
195
|
-
if (result.success) {
|
|
196
|
-
expect(result.data.techStack?.isSystemOfRecord).toBe(false)
|
|
197
|
-
}
|
|
198
|
-
})
|
|
199
|
-
|
|
200
|
-
it('rejects a mapping with an invalid credentialStatus in techStack', () => {
|
|
201
|
-
const result = ResourceMappingSchema.safeParse({
|
|
202
|
-
...MINIMAL_MAPPING,
|
|
203
|
-
techStack: {
|
|
204
|
-
platform: 'Stripe',
|
|
205
|
-
purpose: 'Billing',
|
|
206
|
-
credentialStatus: 'broken'
|
|
207
|
-
}
|
|
208
|
-
})
|
|
209
|
-
expect(result.success).toBe(false)
|
|
210
|
-
})
|
|
211
|
-
|
|
212
|
-
it('rejects a mapping with techStack missing platform', () => {
|
|
213
|
-
const result = ResourceMappingSchema.safeParse({
|
|
214
|
-
...MINIMAL_MAPPING,
|
|
215
|
-
techStack: {
|
|
216
|
-
purpose: 'Something',
|
|
217
|
-
credentialStatus: 'configured'
|
|
218
|
-
}
|
|
219
|
-
})
|
|
220
|
-
expect(result.success).toBe(false)
|
|
221
|
-
})
|
|
222
|
-
})
|
|
223
|
-
|
|
224
|
-
// ---------------------------------------------------------------------------
|
|
225
|
-
// Group 5: Multiple mappings with mixed techStack presence
|
|
226
|
-
// ---------------------------------------------------------------------------
|
|
227
|
-
|
|
228
|
-
describe('ResourceMappingSchema — mixed techStack presence across entries', () => {
|
|
229
|
-
it('resolves a model with multiple mappings — some with techStack, some without', () => {
|
|
230
|
-
const withStack = {
|
|
231
|
-
...MINIMAL_MAPPING,
|
|
232
|
-
id: 'rm-with-stack',
|
|
233
|
-
resourceId: 'workflow-with-stack',
|
|
234
|
-
techStack: {
|
|
235
|
-
platform: 'Intercom',
|
|
236
|
-
purpose: 'Customer support',
|
|
237
|
-
credentialStatus: 'expired' as const
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
const withoutStack = {
|
|
241
|
-
id: 'rm-without-stack',
|
|
242
|
-
label: 'Plain mapping',
|
|
243
|
-
resourceId: 'plain-workflow',
|
|
244
|
-
resourceType: 'integration' as const,
|
|
245
|
-
|
|
246
|
-
entityIds: [],
|
|
247
|
-
surfaceIds: [],
|
|
248
|
-
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
const withResult = ResourceMappingSchema.safeParse(withStack)
|
|
252
|
-
const withoutResult = ResourceMappingSchema.safeParse(withoutStack)
|
|
253
|
-
|
|
254
|
-
expect(withResult.success).toBe(true)
|
|
255
|
-
expect(withoutResult.success).toBe(true)
|
|
256
|
-
|
|
257
|
-
if (withResult.success) {
|
|
258
|
-
expect(withResult.data.techStack?.credentialStatus).toBe('expired')
|
|
259
|
-
}
|
|
260
|
-
if (withoutResult.success) {
|
|
261
|
-
expect(withoutResult.data.techStack).toBeUndefined()
|
|
262
|
-
}
|
|
263
|
-
})
|
|
264
|
-
})
|
|
265
|
-
|
|
266
|
-
// ---------------------------------------------------------------------------
|
|
267
|
-
// Group 6: Integration via resolveOrganizationModel
|
|
268
|
-
// ---------------------------------------------------------------------------
|
|
269
|
-
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { ResourceMappingSchema, TechStackEntrySchema } from '../../domains/shared'
|
|
3
|
+
import { resolveOrganizationModel } from '../../resolve'
|
|
4
|
+
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
// Minimal valid resource mapping fixture (no techStack) for reuse.
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
|
|
9
|
+
const MINIMAL_MAPPING = {
|
|
10
|
+
id: 'rm-hubspot-sync',
|
|
11
|
+
label: 'HubSpot Sync',
|
|
12
|
+
resourceId: 'hubspot-sync-workflow',
|
|
13
|
+
resourceType: 'workflow' as const,
|
|
14
|
+
systemIds: [],
|
|
15
|
+
entityIds: [],
|
|
16
|
+
surfaceIds: [],
|
|
17
|
+
actionIds: []
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
// Group 1: TechStackEntrySchema — positive parse
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
|
|
24
|
+
describe('TechStackEntrySchema — positive parse', () => {
|
|
25
|
+
it('accepts a fully-populated tech stack entry', () => {
|
|
26
|
+
const result = TechStackEntrySchema.safeParse({
|
|
27
|
+
platform: 'HubSpot',
|
|
28
|
+
purpose: 'Primary CRM and contact management',
|
|
29
|
+
credentialStatus: 'configured',
|
|
30
|
+
isSystemOfRecord: true
|
|
31
|
+
})
|
|
32
|
+
expect(result.success).toBe(true)
|
|
33
|
+
if (result.success) {
|
|
34
|
+
expect(result.data.platform).toBe('HubSpot')
|
|
35
|
+
expect(result.data.credentialStatus).toBe('configured')
|
|
36
|
+
expect(result.data.isSystemOfRecord).toBe(true)
|
|
37
|
+
}
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
it('isSystemOfRecord defaults to false when omitted', () => {
|
|
41
|
+
const result = TechStackEntrySchema.safeParse({
|
|
42
|
+
platform: 'Stripe',
|
|
43
|
+
purpose: 'Payment processing',
|
|
44
|
+
credentialStatus: 'pending'
|
|
45
|
+
})
|
|
46
|
+
expect(result.success).toBe(true)
|
|
47
|
+
if (result.success) {
|
|
48
|
+
expect(result.data.isSystemOfRecord).toBe(false)
|
|
49
|
+
}
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
it('accepts all four credentialStatus enum values', () => {
|
|
53
|
+
const statuses = ['configured', 'pending', 'expired', 'missing'] as const
|
|
54
|
+
for (const status of statuses) {
|
|
55
|
+
const result = TechStackEntrySchema.safeParse({
|
|
56
|
+
platform: 'SomePlatform',
|
|
57
|
+
purpose: 'Some purpose',
|
|
58
|
+
credentialStatus: status
|
|
59
|
+
})
|
|
60
|
+
expect(result.success).toBe(true)
|
|
61
|
+
}
|
|
62
|
+
})
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
// Group 2: TechStackEntrySchema — negative parse
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
|
|
69
|
+
describe('TechStackEntrySchema — negative parse', () => {
|
|
70
|
+
it('rejects invalid credentialStatus enum value', () => {
|
|
71
|
+
const result = TechStackEntrySchema.safeParse({
|
|
72
|
+
platform: 'HubSpot',
|
|
73
|
+
purpose: 'CRM',
|
|
74
|
+
credentialStatus: 'active'
|
|
75
|
+
})
|
|
76
|
+
expect(result.success).toBe(false)
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
it('rejects missing platform', () => {
|
|
80
|
+
const result = TechStackEntrySchema.safeParse({
|
|
81
|
+
purpose: 'CRM',
|
|
82
|
+
credentialStatus: 'configured'
|
|
83
|
+
})
|
|
84
|
+
expect(result.success).toBe(false)
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
it('rejects empty string platform', () => {
|
|
88
|
+
const result = TechStackEntrySchema.safeParse({
|
|
89
|
+
platform: '',
|
|
90
|
+
purpose: 'CRM',
|
|
91
|
+
credentialStatus: 'configured'
|
|
92
|
+
})
|
|
93
|
+
expect(result.success).toBe(false)
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
it('rejects missing purpose', () => {
|
|
97
|
+
const result = TechStackEntrySchema.safeParse({
|
|
98
|
+
platform: 'HubSpot',
|
|
99
|
+
credentialStatus: 'configured'
|
|
100
|
+
})
|
|
101
|
+
expect(result.success).toBe(false)
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
it('rejects empty string purpose', () => {
|
|
105
|
+
const result = TechStackEntrySchema.safeParse({
|
|
106
|
+
platform: 'HubSpot',
|
|
107
|
+
purpose: '',
|
|
108
|
+
credentialStatus: 'configured'
|
|
109
|
+
})
|
|
110
|
+
expect(result.success).toBe(false)
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
it('rejects missing credentialStatus', () => {
|
|
114
|
+
const result = TechStackEntrySchema.safeParse({
|
|
115
|
+
platform: 'HubSpot',
|
|
116
|
+
purpose: 'CRM'
|
|
117
|
+
})
|
|
118
|
+
expect(result.success).toBe(false)
|
|
119
|
+
})
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
// ---------------------------------------------------------------------------
|
|
123
|
+
// Group 3: ResourceMappingSchema — backward compatibility (no techStack)
|
|
124
|
+
// ---------------------------------------------------------------------------
|
|
125
|
+
|
|
126
|
+
describe('ResourceMappingSchema — backward compatibility without techStack', () => {
|
|
127
|
+
it('parses a mapping with no techStack key (legacy shape)', () => {
|
|
128
|
+
const result = ResourceMappingSchema.safeParse(MINIMAL_MAPPING)
|
|
129
|
+
expect(result.success).toBe(true)
|
|
130
|
+
if (result.success) {
|
|
131
|
+
expect(result.data.techStack).toBeUndefined()
|
|
132
|
+
}
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
it('parses a mapping with techStack explicitly set to undefined', () => {
|
|
136
|
+
const result = ResourceMappingSchema.safeParse({ ...MINIMAL_MAPPING, techStack: undefined })
|
|
137
|
+
expect(result.success).toBe(true)
|
|
138
|
+
if (result.success) {
|
|
139
|
+
expect(result.data.techStack).toBeUndefined()
|
|
140
|
+
}
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
it('all existing reference ID fields still default to empty arrays', () => {
|
|
144
|
+
const result = ResourceMappingSchema.safeParse({
|
|
145
|
+
id: 'rm-bare',
|
|
146
|
+
label: 'Bare mapping',
|
|
147
|
+
resourceId: 'bare-workflow',
|
|
148
|
+
resourceType: 'workflow'
|
|
149
|
+
})
|
|
150
|
+
expect(result.success).toBe(true)
|
|
151
|
+
if (result.success) {
|
|
152
|
+
expect(result.data.systemIds).toEqual([])
|
|
153
|
+
expect(result.data.entityIds).toEqual([])
|
|
154
|
+
expect(result.data.surfaceIds).toEqual([])
|
|
155
|
+
expect(result.data.actionIds).toEqual([])
|
|
156
|
+
expect(result.data.techStack).toBeUndefined()
|
|
157
|
+
}
|
|
158
|
+
})
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
// ---------------------------------------------------------------------------
|
|
162
|
+
// Group 4: ResourceMappingSchema — with techStack
|
|
163
|
+
// ---------------------------------------------------------------------------
|
|
164
|
+
|
|
165
|
+
describe('ResourceMappingSchema — with techStack', () => {
|
|
166
|
+
it('parses a mapping with a full techStack object', () => {
|
|
167
|
+
const result = ResourceMappingSchema.safeParse({
|
|
168
|
+
...MINIMAL_MAPPING,
|
|
169
|
+
techStack: {
|
|
170
|
+
platform: 'HubSpot',
|
|
171
|
+
purpose: 'Contact and deal management',
|
|
172
|
+
credentialStatus: 'configured',
|
|
173
|
+
isSystemOfRecord: true
|
|
174
|
+
}
|
|
175
|
+
})
|
|
176
|
+
expect(result.success).toBe(true)
|
|
177
|
+
if (result.success) {
|
|
178
|
+
expect(result.data.techStack).toBeDefined()
|
|
179
|
+
expect(result.data.techStack?.platform).toBe('HubSpot')
|
|
180
|
+
expect(result.data.techStack?.credentialStatus).toBe('configured')
|
|
181
|
+
expect(result.data.techStack?.isSystemOfRecord).toBe(true)
|
|
182
|
+
}
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
it('isSystemOfRecord defaults to false inside techStack when omitted', () => {
|
|
186
|
+
const result = ResourceMappingSchema.safeParse({
|
|
187
|
+
...MINIMAL_MAPPING,
|
|
188
|
+
techStack: {
|
|
189
|
+
platform: 'Notion',
|
|
190
|
+
purpose: 'Internal knowledge base',
|
|
191
|
+
credentialStatus: 'pending'
|
|
192
|
+
}
|
|
193
|
+
})
|
|
194
|
+
expect(result.success).toBe(true)
|
|
195
|
+
if (result.success) {
|
|
196
|
+
expect(result.data.techStack?.isSystemOfRecord).toBe(false)
|
|
197
|
+
}
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
it('rejects a mapping with an invalid credentialStatus in techStack', () => {
|
|
201
|
+
const result = ResourceMappingSchema.safeParse({
|
|
202
|
+
...MINIMAL_MAPPING,
|
|
203
|
+
techStack: {
|
|
204
|
+
platform: 'Stripe',
|
|
205
|
+
purpose: 'Billing',
|
|
206
|
+
credentialStatus: 'broken'
|
|
207
|
+
}
|
|
208
|
+
})
|
|
209
|
+
expect(result.success).toBe(false)
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
it('rejects a mapping with techStack missing platform', () => {
|
|
213
|
+
const result = ResourceMappingSchema.safeParse({
|
|
214
|
+
...MINIMAL_MAPPING,
|
|
215
|
+
techStack: {
|
|
216
|
+
purpose: 'Something',
|
|
217
|
+
credentialStatus: 'configured'
|
|
218
|
+
}
|
|
219
|
+
})
|
|
220
|
+
expect(result.success).toBe(false)
|
|
221
|
+
})
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
// ---------------------------------------------------------------------------
|
|
225
|
+
// Group 5: Multiple mappings with mixed techStack presence
|
|
226
|
+
// ---------------------------------------------------------------------------
|
|
227
|
+
|
|
228
|
+
describe('ResourceMappingSchema — mixed techStack presence across entries', () => {
|
|
229
|
+
it('resolves a model with multiple mappings — some with techStack, some without', () => {
|
|
230
|
+
const withStack = {
|
|
231
|
+
...MINIMAL_MAPPING,
|
|
232
|
+
id: 'rm-with-stack',
|
|
233
|
+
resourceId: 'workflow-with-stack',
|
|
234
|
+
techStack: {
|
|
235
|
+
platform: 'Intercom',
|
|
236
|
+
purpose: 'Customer support',
|
|
237
|
+
credentialStatus: 'expired' as const
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
const withoutStack = {
|
|
241
|
+
id: 'rm-without-stack',
|
|
242
|
+
label: 'Plain mapping',
|
|
243
|
+
resourceId: 'plain-workflow',
|
|
244
|
+
resourceType: 'integration' as const,
|
|
245
|
+
systemIds: [],
|
|
246
|
+
entityIds: [],
|
|
247
|
+
surfaceIds: [],
|
|
248
|
+
actionIds: []
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const withResult = ResourceMappingSchema.safeParse(withStack)
|
|
252
|
+
const withoutResult = ResourceMappingSchema.safeParse(withoutStack)
|
|
253
|
+
|
|
254
|
+
expect(withResult.success).toBe(true)
|
|
255
|
+
expect(withoutResult.success).toBe(true)
|
|
256
|
+
|
|
257
|
+
if (withResult.success) {
|
|
258
|
+
expect(withResult.data.techStack?.credentialStatus).toBe('expired')
|
|
259
|
+
}
|
|
260
|
+
if (withoutResult.success) {
|
|
261
|
+
expect(withoutResult.data.techStack).toBeUndefined()
|
|
262
|
+
}
|
|
263
|
+
})
|
|
264
|
+
})
|
|
265
|
+
|
|
266
|
+
// ---------------------------------------------------------------------------
|
|
267
|
+
// Group 6: Integration via resolveOrganizationModel
|
|
268
|
+
// ---------------------------------------------------------------------------
|
|
269
|
+
|
|
270
270
|
describe('resolveOrganizationModel resource mapping removal', () => {
|
|
271
271
|
it('resolved model validates without resourceMappings', () => {
|
|
272
272
|
expect(() => resolveOrganizationModel({})).not.toThrow()
|
|
@@ -280,10 +280,10 @@ describe('resolveOrganizationModel resource mapping removal', () => {
|
|
|
280
280
|
label: 'Legacy mapping',
|
|
281
281
|
resourceId: 'legacy-workflow',
|
|
282
282
|
resourceType: 'workflow',
|
|
283
|
-
|
|
283
|
+
systemIds: [],
|
|
284
284
|
entityIds: [],
|
|
285
285
|
surfaceIds: [],
|
|
286
|
-
|
|
286
|
+
actionIds: []
|
|
287
287
|
}
|
|
288
288
|
]
|
|
289
289
|
} as Parameters<typeof resolveOrganizationModel>[0])
|