@things-factory/dataset 9.1.19 → 9.2.5

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.
Files changed (27) hide show
  1. package/dist-client/tsconfig.tsbuildinfo +1 -1
  2. package/dist-server/activities/activity-data-review.js +5 -2
  3. package/dist-server/activities/activity-data-review.js.map +1 -1
  4. package/dist-server/activities/activity-ooc-review.js +4 -1
  5. package/dist-server/activities/activity-ooc-review.js.map +1 -1
  6. package/dist-server/controllers/create-data-ooc.js +2 -1
  7. package/dist-server/controllers/create-data-ooc.js.map +1 -1
  8. package/dist-server/controllers/create-data-sample.js +2 -2
  9. package/dist-server/controllers/create-data-sample.js.map +1 -1
  10. package/dist-server/controllers/issue-data-collection-task.js +9 -5
  11. package/dist-server/controllers/issue-data-collection-task.js.map +1 -1
  12. package/dist-server/controllers/issue-ooc-resolve.js +11 -6
  13. package/dist-server/controllers/issue-ooc-resolve.js.map +1 -1
  14. package/dist-server/controllers/issue-ooc-review.js +9 -6
  15. package/dist-server/controllers/issue-ooc-review.js.map +1 -1
  16. package/dist-server/tsconfig.tsbuildinfo +1 -1
  17. package/package.json +12 -12
  18. package/spec/integration/debug.spec.ts +42 -0
  19. package/spec/integration/ooc-lifecycle.spec.ts +484 -0
  20. package/spec/integration/ooc-workflow.spec.ts +276 -0
  21. package/spec/integration/simple.spec.ts +62 -0
  22. package/spec/unit/controllers/activity-callbacks.spec.ts +609 -0
  23. package/spec/unit/controllers/create-data-ooc.spec.ts +310 -0
  24. package/spec/unit/controllers/issue-ooc-resolve.spec.ts +431 -0
  25. package/spec/unit/controllers/issue-ooc-review.spec.ts +288 -0
  26. package/spec/unit/data-use-case.spec.ts +150 -0
  27. package/spec/unit/ooc-state-transition.spec.ts +233 -0
@@ -0,0 +1,276 @@
1
+ /**
2
+ * OOC Workflow Integration Tests
3
+ * OOC(Out Of Control) 처리 워크플로우 통합 테스트
4
+ */
5
+
6
+ import { TestDatabase } from '../../../../test/test-database'
7
+ import { createTestContext, withTestTransaction } from '../../../../test/test-context'
8
+ import {
9
+ domainFactory,
10
+ userFactory,
11
+ roleFactory,
12
+ dataSetFactory,
13
+ dataSampleFactory,
14
+ dataOocFactory,
15
+ activityFactory,
16
+ activityInstanceFactory,
17
+ activityThreadFactory
18
+ } from '../../../../test/factories'
19
+
20
+ import { DataOocStatus, ActivityInstanceStatus, ActivityThreadStatus } from '../../../../test/entities/schemas'
21
+
22
+ describe('OOC Workflow Integration Tests', () => {
23
+ let testDb: TestDatabase
24
+
25
+ beforeAll(async () => {
26
+ testDb = TestDatabase.getInstance()
27
+ })
28
+
29
+ describe('OOC 생성 및 상태 관리', () => {
30
+ it('DataOoc이 ISSUED 상태로 생성되어야 한다', async () => {
31
+ await withTestTransaction(async (context) => {
32
+ const { tx } = context.state
33
+
34
+ // Given: 데이터셋 생성
35
+ const { dataSet, domain } = await dataSetFactory.createWithRoles({}, undefined, tx)
36
+
37
+ // When: OOC 생성
38
+ const dataOoc = await dataOocFactory.createWithDataSetAndSample(
39
+ { state: DataOocStatus.ISSUED },
40
+ dataSet,
41
+ undefined,
42
+ tx
43
+ )
44
+
45
+ // Then: ISSUED 상태 확인
46
+ expect(dataOoc.state).toBe(DataOocStatus.ISSUED)
47
+ expect(dataOoc.dataSet?.id).toBe(dataSet.id)
48
+ })
49
+ })
50
+
51
+ it('DataOoc 상태가 ISSUED → REVIEWED → CORRECTED로 전이되어야 한다', async () => {
52
+ await withTestTransaction(async (context) => {
53
+ const { tx } = context.state
54
+
55
+ // Given: OOC 생성
56
+ const dataOoc = await dataOocFactory.create({ state: DataOocStatus.ISSUED }, tx)
57
+
58
+ // When: REVIEWED 상태로 변경
59
+ dataOoc.state = DataOocStatus.REVIEWED
60
+ dataOoc.reviewedAt = new Date()
61
+ const reviewed = await tx.getRepository('DataOoc').save(dataOoc)
62
+
63
+ // Then: REVIEWED 상태 확인
64
+ expect(reviewed.state).toBe(DataOocStatus.REVIEWED)
65
+ expect(reviewed.reviewedAt).toBeDefined()
66
+
67
+ // When: CORRECTED 상태로 변경
68
+ reviewed.state = DataOocStatus.CORRECTED
69
+ reviewed.correctedAt = new Date()
70
+ const corrected = await tx.getRepository('DataOoc').save(reviewed)
71
+
72
+ // Then: CORRECTED 상태 확인
73
+ expect(corrected.state).toBe(DataOocStatus.CORRECTED)
74
+ expect(corrected.correctedAt).toBeDefined()
75
+ })
76
+ })
77
+ })
78
+
79
+ describe('Activity Instance 생명주기', () => {
80
+ it('ActivityInstance가 Issued 상태로 생성되어야 한다', async () => {
81
+ await withTestTransaction(async (context) => {
82
+ const { tx } = context.state
83
+ const domain = await domainFactory.create({}, tx)
84
+
85
+ // Given: OOC Review Activity 생성
86
+ const activity = await activityFactory.createOocReviewActivity(domain, tx)
87
+
88
+ // When: ActivityInstance 생성
89
+ const instance = await activityInstanceFactory.createWithActivity(
90
+ { state: ActivityInstanceStatus.Issued },
91
+ activity,
92
+ domain,
93
+ tx
94
+ )
95
+
96
+ // Then
97
+ expect(instance.state).toBe(ActivityInstanceStatus.Issued)
98
+ expect(instance.activity?.id).toBe(activity.id)
99
+ })
100
+ })
101
+
102
+ it('ActivityThread가 할당되면 Instance 상태가 Assigned로 변경되어야 한다', async () => {
103
+ await withTestTransaction(async (context) => {
104
+ const { tx } = context.state
105
+ const domain = await domainFactory.create({}, tx)
106
+
107
+ // Given: Instance 생성
108
+ const instance = await activityInstanceFactory.createWithActivity(
109
+ { state: ActivityInstanceStatus.Issued },
110
+ undefined,
111
+ domain,
112
+ tx
113
+ )
114
+
115
+ // When: Thread 할당
116
+ const user = await userFactory.create({}, tx)
117
+ const thread = await activityThreadFactory.createWithInstanceAndAssignee(
118
+ { state: ActivityThreadStatus.Assigned },
119
+ instance,
120
+ user,
121
+ tx
122
+ )
123
+
124
+ // Then
125
+ expect(thread.state).toBe(ActivityThreadStatus.Assigned)
126
+ expect(thread.assignee?.id).toBe(user.id)
127
+ })
128
+ })
129
+ })
130
+
131
+ describe('결재라인 처리', () => {
132
+ it('결재라인이 없는 경우 submit 시 바로 Ended 상태가 되어야 한다', async () => {
133
+ await withTestTransaction(async (context) => {
134
+ const { tx } = context.state
135
+ const domain = await domainFactory.create({}, tx)
136
+
137
+ // Given: 결재라인 없는 Instance
138
+ const instance = await activityInstanceFactory.createWithApprovalLine(
139
+ [], // 빈 결재라인
140
+ { state: ActivityInstanceStatus.Started },
141
+ undefined,
142
+ domain,
143
+ tx
144
+ )
145
+
146
+ const user = await userFactory.create({}, tx)
147
+ const thread = await activityThreadFactory.createWithInstanceAndAssignee(
148
+ { state: ActivityThreadStatus.Started },
149
+ instance,
150
+ user,
151
+ tx
152
+ )
153
+
154
+ // When: Thread 완료 시뮬레이션
155
+ thread.state = ActivityThreadStatus.Ended
156
+ thread.terminatedAt = new Date()
157
+ const endedThread = await tx.getRepository('ActivityThread').save(thread)
158
+
159
+ // Then
160
+ expect(endedThread.state).toBe(ActivityThreadStatus.Ended)
161
+ })
162
+ })
163
+
164
+ it('ROLE 타입 결재라인이 올바르게 저장되어야 한다', async () => {
165
+ await withTestTransaction(async (context) => {
166
+ const { tx } = context.state
167
+ const domain = await domainFactory.create({}, tx)
168
+ const approverRole = await roleFactory.create({ name: 'Approver Role', domain }, tx)
169
+
170
+ // Given: ROLE 타입 결재라인
171
+ const approvalLine = [
172
+ {
173
+ type: 'Role',
174
+ approver: { id: approverRole.id, name: approverRole.name }
175
+ // value는 저장되지 않음 (클라이언트 동작과 일치)
176
+ }
177
+ ]
178
+
179
+ // When: Instance 생성
180
+ const instance = await activityInstanceFactory.createWithApprovalLine(
181
+ approvalLine,
182
+ {},
183
+ undefined,
184
+ domain,
185
+ tx
186
+ )
187
+
188
+ // Then: 결재라인 확인
189
+ expect(instance.approvalLine).toBeDefined()
190
+ expect(instance.approvalLine?.length).toBe(1)
191
+ expect(instance.approvalLine?.[0].type).toBe('Role')
192
+ expect(instance.approvalLine?.[0].approver?.id).toBe(approverRole.id)
193
+ })
194
+ })
195
+ })
196
+
197
+ describe('OOC Review → Resolve 워크플로우', () => {
198
+ it('전체 OOC 워크플로우가 정상 동작해야 한다', async () => {
199
+ await withTestTransaction(async (context) => {
200
+ const { tx, domain, user } = context.state
201
+
202
+ // 1. DataSet with roles 생성
203
+ const { dataSet, supervisoryRole, resolverRole } = await dataSetFactory.createWithRoles(
204
+ {},
205
+ domain,
206
+ tx
207
+ )
208
+
209
+ // 2. OOC Activities 생성
210
+ const reviewActivity = await activityFactory.createOocReviewActivity(domain, tx)
211
+ const resolveActivity = await activityFactory.createOocResolveActivity(domain, tx)
212
+
213
+ // 3. DataOoc 생성 (ISSUED 상태)
214
+ const dataOoc = await dataOocFactory.createWithDataSetAndSample(
215
+ { state: DataOocStatus.ISSUED },
216
+ dataSet,
217
+ undefined,
218
+ tx
219
+ )
220
+
221
+ expect(dataOoc.state).toBe(DataOocStatus.ISSUED)
222
+
223
+ // 4. OOC Review Instance 생성 시뮬레이션
224
+ const reviewInstance = await activityInstanceFactory.createWithActivity(
225
+ {
226
+ name: `[OOC 검토] ${dataSet.name}`,
227
+ state: ActivityInstanceStatus.Issued,
228
+ assigneeRole: supervisoryRole,
229
+ input: { dataOocId: dataOoc.id }
230
+ },
231
+ reviewActivity,
232
+ domain,
233
+ tx
234
+ )
235
+
236
+ expect(reviewInstance.input?.dataOocId).toBe(dataOoc.id)
237
+
238
+ // 5. Review 완료 시뮬레이션 → DataOoc REVIEWED
239
+ dataOoc.state = DataOocStatus.REVIEWED
240
+ dataOoc.reviewedAt = new Date()
241
+ dataOoc.correctiveInstruction = 'Temperature 조절 필요'
242
+ await tx.getRepository('DataOoc').save(dataOoc)
243
+
244
+ // 6. OOC Resolve Instance 생성 시뮬레이션
245
+ const resolveInstance = await activityInstanceFactory.createWithActivity(
246
+ {
247
+ name: `[OOC 조치] ${dataSet.name}`,
248
+ state: ActivityInstanceStatus.Issued,
249
+ assigneeRole: resolverRole,
250
+ input: {
251
+ dataOocId: dataOoc.id,
252
+ instruction: dataOoc.correctiveInstruction
253
+ },
254
+ approvalLine: dataSet.outlierApprovalLine || []
255
+ },
256
+ resolveActivity,
257
+ domain,
258
+ tx
259
+ )
260
+
261
+ expect(resolveInstance.input?.instruction).toBe('Temperature 조절 필요')
262
+
263
+ // 7. Resolve 완료 시뮬레이션 → DataOoc CORRECTED
264
+ dataOoc.state = DataOocStatus.CORRECTED
265
+ dataOoc.correctedAt = new Date()
266
+ dataOoc.correctiveAction = 'Temperature를 정상 범위로 조절함'
267
+ const finalOoc = await tx.getRepository('DataOoc').save(dataOoc)
268
+
269
+ // Then: 최종 상태 확인
270
+ expect(finalOoc.state).toBe(DataOocStatus.CORRECTED)
271
+ expect(finalOoc.correctiveInstruction).toBe('Temperature 조절 필요')
272
+ expect(finalOoc.correctiveAction).toBe('Temperature를 정상 범위로 조절함')
273
+ })
274
+ })
275
+ })
276
+ })
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Simple Database Integration Test
3
+ * 기본 데이터베이스 연결 및 엔티티 테스트
4
+ */
5
+
6
+ import { TestDatabase } from '../../../../test/test-database'
7
+ import { Domain, DataSet } from '../../../../test/entities/schemas'
8
+
9
+ describe('Simple Database Test', () => {
10
+ let testDb: TestDatabase
11
+
12
+ beforeAll(async () => {
13
+ testDb = TestDatabase.getInstance()
14
+ // Ensure initialized
15
+ if (!testDb.getDataSource().isInitialized) {
16
+ await testDb.initialize()
17
+ }
18
+ })
19
+
20
+ it('should have Domain entity registered', async () => {
21
+ const metadata = testDb.getDataSource().getMetadata('Domain')
22
+ expect(metadata).toBeDefined()
23
+ expect(metadata.name).toBe('Domain')
24
+ })
25
+
26
+ it('should create and retrieve a Domain', async () => {
27
+ const manager = testDb.getManager()
28
+
29
+ const domain = await manager.save('Domain', {
30
+ name: 'Test',
31
+ subdomain: 'test',
32
+ timezone: 'UTC'
33
+ }) as Domain
34
+
35
+ expect(domain.id).toBeDefined()
36
+ expect(domain.name).toBe('Test')
37
+
38
+ const found = await manager.findOne('Domain', { where: { id: domain.id } }) as Domain | null
39
+ expect(found).toBeDefined()
40
+ expect(found?.name).toBe('Test')
41
+ })
42
+
43
+ it('should create a DataSet with Domain', async () => {
44
+ const manager = testDb.getManager()
45
+
46
+ const domain = await manager.save('Domain', {
47
+ name: 'Test Domain',
48
+ subdomain: 'test-ds',
49
+ timezone: 'UTC'
50
+ }) as Domain
51
+
52
+ const dataSet = await manager.save('DataSet', {
53
+ name: 'Test DataSet',
54
+ type: 'manual',
55
+ active: true,
56
+ domain
57
+ }) as DataSet
58
+
59
+ expect(dataSet.id).toBeDefined()
60
+ expect(dataSet.name).toBe('Test DataSet')
61
+ })
62
+ })