@taruvi/sdk 1.3.3 → 1.3.4-beta.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/.claude/settings.local.json +19 -0
- package/.kiro/settings/lsp.json +198 -0
- package/MODULE_NAMING_CHANGES.md +81 -0
- package/PARAMETER_NAMING_CHANGES.md +106 -0
- package/README.md +4 -4
- package/USAGE_EXAMPLE.md +86 -0
- package/package.json +10 -9
- package/{dist/client.js → src/client.ts} +59 -39
- package/src/index.ts +54 -0
- package/src/lib/analytics/AnalyticsClient.ts +24 -0
- package/src/lib/analytics/types.ts +8 -0
- package/src/lib/app/AppClient.ts +54 -0
- package/src/lib/app/types.ts +50 -0
- package/{dist/lib/auth/AuthClient.js → src/lib/auth/AuthClient.ts} +106 -70
- package/src/lib/auth/types.ts +111 -0
- package/src/lib/database/DatabaseClient.ts +148 -0
- package/src/lib/database/types.ts +46 -0
- package/src/lib/functions/FunctionsClient.ts +27 -0
- package/src/lib/functions/types.ts +25 -0
- package/src/lib/graphs/GraphClient.ts +106 -0
- package/src/lib/graphs/types.ts +33 -0
- package/src/lib/policy/PolicyClient.ts +79 -0
- package/src/lib/policy/types.ts +40 -0
- package/src/lib/secrets/SecretsClient.ts +75 -0
- package/src/lib/secrets/types.ts +59 -0
- package/src/lib/settings/SettingsClient.ts +14 -0
- package/src/lib/settings/types.ts +9 -0
- package/src/lib/storage/StorageClient.ts +123 -0
- package/src/lib/storage/types.ts +86 -0
- package/src/lib/users/UserClient.ts +55 -0
- package/src/lib/users/types.ts +104 -0
- package/src/lib-internal/errors/ErrorClient.ts +102 -0
- package/src/lib-internal/errors/index.ts +3 -0
- package/src/lib-internal/errors/types.ts +28 -0
- package/src/lib-internal/http/HttpClient.ts +129 -0
- package/{dist/lib-internal/http/types.js → src/lib-internal/http/types.ts} +3 -2
- package/src/lib-internal/routes/AnalyticsRoutes.ts +3 -0
- package/src/lib-internal/routes/AppRoutes.ts +9 -0
- package/src/lib-internal/routes/AuthRoutes.ts +0 -0
- package/src/lib-internal/routes/DatabaseRoutes.ts +9 -0
- package/src/lib-internal/routes/FunctionRoutes.ts +3 -0
- package/src/lib-internal/routes/GraphRoutes.ts +14 -0
- package/src/lib-internal/routes/PolicyRoutes.ts +4 -0
- package/src/lib-internal/routes/SecretsRoutes.ts +5 -0
- package/{dist/lib-internal/routes/SettingsRoutes.js → src/lib-internal/routes/SettingsRoutes.ts} +1 -2
- package/src/lib-internal/routes/StorageRoutes.ts +15 -0
- package/src/lib-internal/routes/UserRoutes.ts +11 -0
- package/src/lib-internal/routes/index.ts +0 -0
- package/{dist/lib-internal/token/TokenClient.js → src/lib-internal/token/TokenClient.ts} +144 -99
- package/src/lib-internal/token/types.ts +0 -0
- package/src/types.ts +90 -0
- package/{dist/utils/enums.js → src/utils/enums.ts} +10 -4
- package/src/utils/utils.ts +37 -0
- package/tests/fixtures/mockClient.ts +19 -0
- package/tests/mocks/db.json +1 -0
- package/tests/unit/analytics/AnalyticsClient.test.ts +84 -0
- package/tests/unit/app/AppClient.test.ts +114 -0
- package/tests/unit/auth/AuthClient.test.ts +131 -0
- package/tests/unit/client/Client.test.ts +70 -0
- package/tests/unit/database/DatabaseClient.test.ts +304 -0
- package/tests/unit/edge-cases/robustness.test.ts +259 -0
- package/tests/unit/errors/errors.test.ts +209 -0
- package/tests/unit/functions/FunctionsClient.test.ts +99 -0
- package/tests/unit/graphs/GraphClient.test.ts +329 -0
- package/tests/unit/policy/PolicyClient.test.ts +184 -0
- package/tests/unit/secrets/SecretsClient.test.ts +146 -0
- package/tests/unit/settings/SettingsClient.test.ts +50 -0
- package/tests/unit/storage/StorageClient.test.ts +251 -0
- package/tests/unit/users/UserClient.test.ts +150 -0
- package/tsconfig.json +43 -0
- package/vitest.config.ts +7 -0
- package/dist/client.d.ts +0 -29
- package/dist/client.d.ts.map +0 -1
- package/dist/client.js.map +0 -1
- package/dist/index.d.ts +0 -25
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -15
- package/dist/index.js.map +0 -1
- package/dist/lib/Analytics/AnalyticsClient.d.ts +0 -9
- package/dist/lib/Analytics/AnalyticsClient.d.ts.map +0 -1
- package/dist/lib/Analytics/AnalyticsClient.js +0 -17
- package/dist/lib/Analytics/AnalyticsClient.js.map +0 -1
- package/dist/lib/Analytics/types.d.ts +0 -7
- package/dist/lib/Analytics/types.d.ts.map +0 -1
- package/dist/lib/Analytics/types.js +0 -2
- package/dist/lib/Analytics/types.js.map +0 -1
- package/dist/lib/App/AppClient.d.ts +0 -15
- package/dist/lib/App/AppClient.d.ts.map +0 -1
- package/dist/lib/App/AppClient.js +0 -41
- package/dist/lib/App/AppClient.js.map +0 -1
- package/dist/lib/App/types.d.ts +0 -36
- package/dist/lib/App/types.d.ts.map +0 -1
- package/dist/lib/App/types.js +0 -2
- package/dist/lib/App/types.js.map +0 -1
- package/dist/lib/Database/DatabaseClient.d.ts +0 -28
- package/dist/lib/Database/DatabaseClient.d.ts.map +0 -1
- package/dist/lib/Database/DatabaseClient.js +0 -116
- package/dist/lib/Database/DatabaseClient.js.map +0 -1
- package/dist/lib/Database/types.d.ts +0 -24
- package/dist/lib/Database/types.d.ts.map +0 -1
- package/dist/lib/Database/types.js +0 -2
- package/dist/lib/Database/types.js.map +0 -1
- package/dist/lib/Function/FunctionsClient.d.ts +0 -9
- package/dist/lib/Function/FunctionsClient.d.ts.map +0 -1
- package/dist/lib/Function/FunctionsClient.js +0 -20
- package/dist/lib/Function/FunctionsClient.js.map +0 -1
- package/dist/lib/Function/types.d.ts +0 -16
- package/dist/lib/Function/types.d.ts.map +0 -1
- package/dist/lib/Function/types.js +0 -2
- package/dist/lib/Function/types.js.map +0 -1
- package/dist/lib/Graphs/GraphClient.d.ts +0 -26
- package/dist/lib/Graphs/GraphClient.d.ts.map +0 -1
- package/dist/lib/Graphs/GraphClient.js +0 -86
- package/dist/lib/Graphs/GraphClient.js.map +0 -1
- package/dist/lib/Graphs/types.d.ts +0 -23
- package/dist/lib/Graphs/types.d.ts.map +0 -1
- package/dist/lib/Graphs/types.js +0 -2
- package/dist/lib/Graphs/types.js.map +0 -1
- package/dist/lib/Policy/PolicyClient.d.ts +0 -9
- package/dist/lib/Policy/PolicyClient.d.ts.map +0 -1
- package/dist/lib/Policy/PolicyClient.js +0 -24
- package/dist/lib/Policy/PolicyClient.js.map +0 -1
- package/dist/lib/Policy/types.d.ts +0 -22
- package/dist/lib/Policy/types.d.ts.map +0 -1
- package/dist/lib/Policy/types.js +0 -2
- package/dist/lib/Policy/types.js.map +0 -1
- package/dist/lib/Secrets/SecretsClient.d.ts +0 -14
- package/dist/lib/Secrets/SecretsClient.d.ts.map +0 -1
- package/dist/lib/Secrets/SecretsClient.js +0 -32
- package/dist/lib/Secrets/SecretsClient.js.map +0 -1
- package/dist/lib/Secrets/types.d.ts +0 -13
- package/dist/lib/Secrets/types.d.ts.map +0 -1
- package/dist/lib/Secrets/types.js +0 -2
- package/dist/lib/Secrets/types.js.map +0 -1
- package/dist/lib/Settings/SettingsClient.d.ts +0 -7
- package/dist/lib/Settings/SettingsClient.d.ts.map +0 -1
- package/dist/lib/Settings/SettingsClient.js +0 -11
- package/dist/lib/Settings/SettingsClient.js.map +0 -1
- package/dist/lib/Settings/types.d.ts +0 -4
- package/dist/lib/Settings/types.d.ts.map +0 -1
- package/dist/lib/Settings/types.js +0 -2
- package/dist/lib/Settings/types.js.map +0 -1
- package/dist/lib/Storage/StorageClient.d.ts +0 -26
- package/dist/lib/Storage/StorageClient.d.ts.map +0 -1
- package/dist/lib/Storage/StorageClient.js +0 -78
- package/dist/lib/Storage/StorageClient.js.map +0 -1
- package/dist/lib/Storage/types.d.ts +0 -35
- package/dist/lib/Storage/types.d.ts.map +0 -1
- package/dist/lib/Storage/types.js +0 -2
- package/dist/lib/Storage/types.js.map +0 -1
- package/dist/lib/auth/AuthClient.d.ts +0 -66
- package/dist/lib/auth/AuthClient.d.ts.map +0 -1
- package/dist/lib/auth/AuthClient.js.map +0 -1
- package/dist/lib/auth/types.d.ts +0 -9
- package/dist/lib/auth/types.d.ts.map +0 -1
- package/dist/lib/auth/types.js +0 -2
- package/dist/lib/auth/types.js.map +0 -1
- package/dist/lib/user/UserClient.d.ts +0 -16
- package/dist/lib/user/UserClient.d.ts.map +0 -1
- package/dist/lib/user/UserClient.js +0 -41
- package/dist/lib/user/UserClient.js.map +0 -1
- package/dist/lib/user/types.d.ts +0 -100
- package/dist/lib/user/types.d.ts.map +0 -1
- package/dist/lib/user/types.js +0 -2
- package/dist/lib/user/types.js.map +0 -1
- package/dist/lib-internal/errors/ErrorClient.d.ts +0 -4
- package/dist/lib-internal/errors/ErrorClient.d.ts.map +0 -1
- package/dist/lib-internal/errors/ErrorClient.js +0 -6
- package/dist/lib-internal/errors/ErrorClient.js.map +0 -1
- package/dist/lib-internal/errors/index.d.ts +0 -8
- package/dist/lib-internal/errors/index.d.ts.map +0 -1
- package/dist/lib-internal/errors/index.js +0 -23
- package/dist/lib-internal/errors/index.js.map +0 -1
- package/dist/lib-internal/errors/types.d.ts +0 -83
- package/dist/lib-internal/errors/types.d.ts.map +0 -1
- package/dist/lib-internal/errors/types.js +0 -65
- package/dist/lib-internal/errors/types.js.map +0 -1
- package/dist/lib-internal/http/HttpClient.d.ts +0 -22
- package/dist/lib-internal/http/HttpClient.d.ts.map +0 -1
- package/dist/lib-internal/http/HttpClient.js +0 -65
- package/dist/lib-internal/http/HttpClient.js.map +0 -1
- package/dist/lib-internal/http/types.d.ts +0 -12
- package/dist/lib-internal/http/types.d.ts.map +0 -1
- package/dist/lib-internal/http/types.js.map +0 -1
- package/dist/lib-internal/routes/AnalyticsRoutes.d.ts +0 -4
- package/dist/lib-internal/routes/AnalyticsRoutes.d.ts.map +0 -1
- package/dist/lib-internal/routes/AnalyticsRoutes.js +0 -4
- package/dist/lib-internal/routes/AnalyticsRoutes.js.map +0 -1
- package/dist/lib-internal/routes/AppRoutes.d.ts +0 -10
- package/dist/lib-internal/routes/AppRoutes.d.ts.map +0 -1
- package/dist/lib-internal/routes/AppRoutes.js +0 -6
- package/dist/lib-internal/routes/AppRoutes.js.map +0 -1
- package/dist/lib-internal/routes/AuthRoutes.d.ts +0 -2
- package/dist/lib-internal/routes/AuthRoutes.d.ts.map +0 -1
- package/dist/lib-internal/routes/AuthRoutes.js +0 -2
- package/dist/lib-internal/routes/AuthRoutes.js.map +0 -1
- package/dist/lib-internal/routes/DatabaseRoutes.d.ts +0 -10
- package/dist/lib-internal/routes/DatabaseRoutes.d.ts.map +0 -1
- package/dist/lib-internal/routes/DatabaseRoutes.js +0 -6
- package/dist/lib-internal/routes/DatabaseRoutes.js.map +0 -1
- package/dist/lib-internal/routes/FunctionRoutes.d.ts +0 -4
- package/dist/lib-internal/routes/FunctionRoutes.d.ts.map +0 -1
- package/dist/lib-internal/routes/FunctionRoutes.js +0 -4
- package/dist/lib-internal/routes/FunctionRoutes.js.map +0 -1
- package/dist/lib-internal/routes/GraphRoutes.d.ts +0 -14
- package/dist/lib-internal/routes/GraphRoutes.d.ts.map +0 -1
- package/dist/lib-internal/routes/GraphRoutes.js +0 -11
- package/dist/lib-internal/routes/GraphRoutes.js.map +0 -1
- package/dist/lib-internal/routes/PolicyRoutes.d.ts +0 -5
- package/dist/lib-internal/routes/PolicyRoutes.d.ts.map +0 -1
- package/dist/lib-internal/routes/PolicyRoutes.js +0 -5
- package/dist/lib-internal/routes/PolicyRoutes.js.map +0 -1
- package/dist/lib-internal/routes/SecretsRoutes.d.ts +0 -6
- package/dist/lib-internal/routes/SecretsRoutes.d.ts.map +0 -1
- package/dist/lib-internal/routes/SecretsRoutes.js +0 -6
- package/dist/lib-internal/routes/SecretsRoutes.js.map +0 -1
- package/dist/lib-internal/routes/SettingsRoutes.d.ts +0 -4
- package/dist/lib-internal/routes/SettingsRoutes.d.ts.map +0 -1
- package/dist/lib-internal/routes/SettingsRoutes.js.map +0 -1
- package/dist/lib-internal/routes/StorageRoutes.d.ts +0 -11
- package/dist/lib-internal/routes/StorageRoutes.d.ts.map +0 -1
- package/dist/lib-internal/routes/StorageRoutes.js +0 -8
- package/dist/lib-internal/routes/StorageRoutes.js.map +0 -1
- package/dist/lib-internal/routes/UserRoutes.d.ts +0 -11
- package/dist/lib-internal/routes/UserRoutes.d.ts.map +0 -1
- package/dist/lib-internal/routes/UserRoutes.js +0 -11
- package/dist/lib-internal/routes/UserRoutes.js.map +0 -1
- package/dist/lib-internal/routes/index.d.ts +0 -2
- package/dist/lib-internal/routes/index.d.ts.map +0 -1
- package/dist/lib-internal/routes/index.js +0 -2
- package/dist/lib-internal/routes/index.js.map +0 -1
- package/dist/lib-internal/token/TokenClient.d.ts +0 -71
- package/dist/lib-internal/token/TokenClient.d.ts.map +0 -1
- package/dist/lib-internal/token/TokenClient.js.map +0 -1
- package/dist/lib-internal/token/types.d.ts +0 -2
- package/dist/lib-internal/token/types.d.ts.map +0 -1
- package/dist/lib-internal/token/types.js +0 -2
- package/dist/lib-internal/token/types.js.map +0 -1
- package/dist/types.d.ts +0 -49
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -2
- package/dist/types.js.map +0 -1
- package/dist/utils/enums.d.ts +0 -20
- package/dist/utils/enums.d.ts.map +0 -1
- package/dist/utils/enums.js.map +0 -1
- package/dist/utils/utils.d.ts +0 -5
- package/dist/utils/utils.d.ts.map +0 -1
- package/dist/utils/utils.js +0 -32
- package/dist/utils/utils.js.map +0 -1
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import {
|
|
3
|
+
TaruviError,
|
|
4
|
+
ValidationError,
|
|
5
|
+
AuthError,
|
|
6
|
+
ForbiddenError,
|
|
7
|
+
NotFoundError,
|
|
8
|
+
ConflictError,
|
|
9
|
+
TimeoutError,
|
|
10
|
+
NetworkError,
|
|
11
|
+
createErrorFromResponse,
|
|
12
|
+
ErrorCode
|
|
13
|
+
} from '../../../src/lib-internal/errors/index.js'
|
|
14
|
+
|
|
15
|
+
describe('Error classes', () => {
|
|
16
|
+
it('TaruviError has correct properties', () => {
|
|
17
|
+
const err = new TaruviError('Something broke', 500, ErrorCode.INTERNAL_ERROR, 'db down', { field: 'bad' }, { extra: 1 })
|
|
18
|
+
expect(err).toBeInstanceOf(Error)
|
|
19
|
+
expect(err.name).toBe('TaruviError')
|
|
20
|
+
expect(err.message).toBe('Something broke')
|
|
21
|
+
expect(err.statusCode).toBe(500)
|
|
22
|
+
expect(err.code).toBe('INTERNAL_ERROR')
|
|
23
|
+
expect(err.detail).toBe('db down')
|
|
24
|
+
expect(err.errors).toEqual({ field: 'bad' })
|
|
25
|
+
expect(err.data).toEqual({ extra: 1 })
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
it('ValidationError defaults', () => {
|
|
29
|
+
const err = new ValidationError()
|
|
30
|
+
expect(err.name).toBe('ValidationError')
|
|
31
|
+
expect(err.statusCode).toBe(400)
|
|
32
|
+
expect(err.code).toBe('VALIDATION_ERROR')
|
|
33
|
+
expect(err.message).toBe('Validation failed')
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
it('ValidationError with field errors', () => {
|
|
37
|
+
const err = new ValidationError('Bad input', 'check fields', { email: 'required' })
|
|
38
|
+
expect(err.errors).toEqual({ email: 'required' })
|
|
39
|
+
expect(err.detail).toBe('check fields')
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
it('AuthError defaults', () => {
|
|
43
|
+
const err = new AuthError()
|
|
44
|
+
expect(err.name).toBe('AuthError')
|
|
45
|
+
expect(err.statusCode).toBe(401)
|
|
46
|
+
expect(err.code).toBe('UNAUTHORIZED')
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it('ForbiddenError defaults', () => {
|
|
50
|
+
const err = new ForbiddenError()
|
|
51
|
+
expect(err.name).toBe('ForbiddenError')
|
|
52
|
+
expect(err.statusCode).toBe(403)
|
|
53
|
+
expect(err.code).toBe('FORBIDDEN')
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
it('NotFoundError defaults', () => {
|
|
57
|
+
const err = new NotFoundError()
|
|
58
|
+
expect(err.name).toBe('NotFoundError')
|
|
59
|
+
expect(err.statusCode).toBe(404)
|
|
60
|
+
expect(err.code).toBe('NOT_FOUND')
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
it('ConflictError defaults', () => {
|
|
64
|
+
const err = new ConflictError()
|
|
65
|
+
expect(err.name).toBe('ConflictError')
|
|
66
|
+
expect(err.statusCode).toBe(409)
|
|
67
|
+
expect(err.code).toBe('CONFLICT')
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
it('TimeoutError defaults', () => {
|
|
71
|
+
const err = new TimeoutError()
|
|
72
|
+
expect(err.name).toBe('TimeoutError')
|
|
73
|
+
expect(err.statusCode).toBe(504)
|
|
74
|
+
expect(err.code).toBe('GATEWAY_TIMEOUT')
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
it('NetworkError defaults', () => {
|
|
78
|
+
const err = new NetworkError()
|
|
79
|
+
expect(err.name).toBe('NetworkError')
|
|
80
|
+
expect(err.statusCode).toBe(0)
|
|
81
|
+
expect(err.code).toBe('NETWORK_ERROR')
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
it('all errors are instanceof TaruviError', () => {
|
|
85
|
+
expect(new ValidationError()).toBeInstanceOf(TaruviError)
|
|
86
|
+
expect(new AuthError()).toBeInstanceOf(TaruviError)
|
|
87
|
+
expect(new ForbiddenError()).toBeInstanceOf(TaruviError)
|
|
88
|
+
expect(new NotFoundError()).toBeInstanceOf(TaruviError)
|
|
89
|
+
expect(new ConflictError()).toBeInstanceOf(TaruviError)
|
|
90
|
+
expect(new TimeoutError()).toBeInstanceOf(TaruviError)
|
|
91
|
+
expect(new NetworkError()).toBeInstanceOf(TaruviError)
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
it('all errors are instanceof Error', () => {
|
|
95
|
+
expect(new TaruviError('x', 500)).toBeInstanceOf(Error)
|
|
96
|
+
expect(new ValidationError()).toBeInstanceOf(Error)
|
|
97
|
+
expect(new NetworkError()).toBeInstanceOf(Error)
|
|
98
|
+
})
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
describe('createErrorFromResponse', () => {
|
|
102
|
+
it('400 with VALIDATION_ERROR code returns ValidationError', () => {
|
|
103
|
+
const err = createErrorFromResponse(400, {
|
|
104
|
+
status: 'error',
|
|
105
|
+
code: 'VALIDATION_ERROR',
|
|
106
|
+
message: 'Email is required',
|
|
107
|
+
errors: { email: 'This field is required' }
|
|
108
|
+
})
|
|
109
|
+
expect(err).toBeInstanceOf(ValidationError)
|
|
110
|
+
expect(err.message).toBe('Email is required')
|
|
111
|
+
expect(err.errors).toEqual({ email: 'This field is required' })
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
it('400 with BAD_REQUEST code returns TaruviError', () => {
|
|
115
|
+
const err = createErrorFromResponse(400, {
|
|
116
|
+
status: 'error',
|
|
117
|
+
code: 'BAD_REQUEST',
|
|
118
|
+
message: 'Invalid input'
|
|
119
|
+
})
|
|
120
|
+
expect(err).toBeInstanceOf(TaruviError)
|
|
121
|
+
expect(err).not.toBeInstanceOf(ValidationError)
|
|
122
|
+
expect(err.code).toBe('BAD_REQUEST')
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
it('401 returns AuthError', () => {
|
|
126
|
+
const err = createErrorFromResponse(401, {
|
|
127
|
+
status: 'error',
|
|
128
|
+
code: 'UNAUTHORIZED',
|
|
129
|
+
message: 'Token expired'
|
|
130
|
+
})
|
|
131
|
+
expect(err).toBeInstanceOf(AuthError)
|
|
132
|
+
expect(err.message).toBe('Token expired')
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
it('403 returns ForbiddenError', () => {
|
|
136
|
+
const err = createErrorFromResponse(403, {
|
|
137
|
+
status: 'error',
|
|
138
|
+
code: 'FORBIDDEN',
|
|
139
|
+
message: 'Not allowed'
|
|
140
|
+
})
|
|
141
|
+
expect(err).toBeInstanceOf(ForbiddenError)
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
it('404 returns NotFoundError', () => {
|
|
145
|
+
const err = createErrorFromResponse(404, {
|
|
146
|
+
status: 'error',
|
|
147
|
+
code: 'NOT_FOUND',
|
|
148
|
+
message: 'User not found'
|
|
149
|
+
})
|
|
150
|
+
expect(err).toBeInstanceOf(NotFoundError)
|
|
151
|
+
expect(err.message).toBe('User not found')
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
it('409 returns ConflictError', () => {
|
|
155
|
+
const err = createErrorFromResponse(409, {
|
|
156
|
+
status: 'error',
|
|
157
|
+
code: 'CONFLICT',
|
|
158
|
+
message: 'Duplicate entry',
|
|
159
|
+
detail: 'Email already exists'
|
|
160
|
+
})
|
|
161
|
+
expect(err).toBeInstanceOf(ConflictError)
|
|
162
|
+
expect(err.detail).toBe('Email already exists')
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
it('504 returns TimeoutError', () => {
|
|
166
|
+
const err = createErrorFromResponse(504, {
|
|
167
|
+
status: 'error',
|
|
168
|
+
code: 'GATEWAY_TIMEOUT',
|
|
169
|
+
message: 'Query timeout'
|
|
170
|
+
})
|
|
171
|
+
expect(err).toBeInstanceOf(TimeoutError)
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
it('500 returns TaruviError', () => {
|
|
175
|
+
const err = createErrorFromResponse(500, {
|
|
176
|
+
status: 'error',
|
|
177
|
+
code: 'INTERNAL_ERROR',
|
|
178
|
+
message: 'Server error'
|
|
179
|
+
})
|
|
180
|
+
expect(err).toBeInstanceOf(TaruviError)
|
|
181
|
+
expect(err.statusCode).toBe(500)
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
it('handles missing body', () => {
|
|
185
|
+
const err = createErrorFromResponse(500, undefined)
|
|
186
|
+
expect(err).toBeInstanceOf(TaruviError)
|
|
187
|
+
expect(err.message).toBe('Request failed')
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
it('handles body with extra data field', () => {
|
|
191
|
+
const err = createErrorFromResponse(400, {
|
|
192
|
+
status: 'error',
|
|
193
|
+
code: 'BAD_REQUEST',
|
|
194
|
+
message: 'Bad',
|
|
195
|
+
data: { hint: 'check params' }
|
|
196
|
+
})
|
|
197
|
+
expect(err.data).toEqual({ hint: 'check params' })
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
it('handles unknown status code', () => {
|
|
201
|
+
const err = createErrorFromResponse(422, {
|
|
202
|
+
status: 'error',
|
|
203
|
+
code: 'VALIDATION_ERROR',
|
|
204
|
+
message: 'Unprocessable'
|
|
205
|
+
})
|
|
206
|
+
expect(err).toBeInstanceOf(TaruviError)
|
|
207
|
+
expect(err.statusCode).toBe(422)
|
|
208
|
+
})
|
|
209
|
+
})
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
|
2
|
+
import { Functions } from '../../../src/lib/functions/FunctionsClient.js'
|
|
3
|
+
import { Client } from '../../../src/client.js'
|
|
4
|
+
|
|
5
|
+
const mockHttpClient = {
|
|
6
|
+
post: vi.fn()
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const mockClient = {
|
|
10
|
+
getConfig: () => ({ apiKey: 'test-key', appSlug: 'test-app', apiUrl: 'https://api.test.com' }),
|
|
11
|
+
httpClient: mockHttpClient
|
|
12
|
+
} as unknown as Client
|
|
13
|
+
|
|
14
|
+
describe('Functions', () => {
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
vi.clearAllMocks()
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
describe('execute()', () => {
|
|
20
|
+
it('executes function with default options', async () => {
|
|
21
|
+
const response = { data: { result: 'success' } }
|
|
22
|
+
mockHttpClient.post.mockResolvedValue(response)
|
|
23
|
+
|
|
24
|
+
const functions = new Functions(mockClient)
|
|
25
|
+
const result = await functions.execute('my-function')
|
|
26
|
+
|
|
27
|
+
expect(mockHttpClient.post).toHaveBeenCalledWith(
|
|
28
|
+
'api/apps/test-app/functions/my-function/execute/',
|
|
29
|
+
{ async: false, params: {} }
|
|
30
|
+
)
|
|
31
|
+
expect(result).toEqual(response)
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('executes function with params', async () => {
|
|
35
|
+
const response = { data: { result: 'success' } }
|
|
36
|
+
mockHttpClient.post.mockResolvedValue(response)
|
|
37
|
+
|
|
38
|
+
const functions = new Functions(mockClient)
|
|
39
|
+
await functions.execute('my-function', {
|
|
40
|
+
params: { key1: 'value1', key2: 123 }
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
expect(mockHttpClient.post).toHaveBeenCalledWith(
|
|
44
|
+
'api/apps/test-app/functions/my-function/execute/',
|
|
45
|
+
{ async: false, params: { key1: 'value1', key2: 123 } }
|
|
46
|
+
)
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it('executes function asynchronously', async () => {
|
|
50
|
+
const response = {
|
|
51
|
+
invocation: {
|
|
52
|
+
invocation_id: 'inv-123',
|
|
53
|
+
celery_task_id: 'task-456',
|
|
54
|
+
status: 'pending'
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
mockHttpClient.post.mockResolvedValue(response)
|
|
58
|
+
|
|
59
|
+
const functions = new Functions(mockClient)
|
|
60
|
+
const result = await functions.execute('long-task', { async: true })
|
|
61
|
+
|
|
62
|
+
expect(mockHttpClient.post).toHaveBeenCalledWith(
|
|
63
|
+
'api/apps/test-app/functions/long-task/execute/',
|
|
64
|
+
{ async: true, params: {} }
|
|
65
|
+
)
|
|
66
|
+
expect(result).toEqual(response)
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
it('executes function with async and params', async () => {
|
|
70
|
+
mockHttpClient.post.mockResolvedValue({})
|
|
71
|
+
|
|
72
|
+
const functions = new Functions(mockClient)
|
|
73
|
+
await functions.execute('process-data', {
|
|
74
|
+
async: true,
|
|
75
|
+
params: { data: 'test-data' }
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
expect(mockHttpClient.post).toHaveBeenCalledWith(
|
|
79
|
+
'api/apps/test-app/functions/process-data/execute/',
|
|
80
|
+
{ async: true, params: { data: 'test-data' } }
|
|
81
|
+
)
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
it('supports typed response', async () => {
|
|
85
|
+
interface MyResponse {
|
|
86
|
+
total: number
|
|
87
|
+
items: string[]
|
|
88
|
+
}
|
|
89
|
+
const response = { data: { total: 5, items: ['a', 'b', 'c'] } }
|
|
90
|
+
mockHttpClient.post.mockResolvedValue(response)
|
|
91
|
+
|
|
92
|
+
const functions = new Functions(mockClient)
|
|
93
|
+
const result = await functions.execute<MyResponse>('get-items')
|
|
94
|
+
|
|
95
|
+
expect(result.data?.total).toBe(5)
|
|
96
|
+
expect(result.data?.items).toEqual(['a', 'b', 'c'])
|
|
97
|
+
})
|
|
98
|
+
})
|
|
99
|
+
})
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
|
2
|
+
import { Graph } from '../../../src/lib/graphs/GraphClient.js'
|
|
3
|
+
import { Client } from '../../../src/client.js'
|
|
4
|
+
import type { EdgeResponse } from '../../../src/lib/graphs/types.js'
|
|
5
|
+
import type { TaruviResponse } from '../../../src/types.js'
|
|
6
|
+
|
|
7
|
+
const mockHttpClient = {
|
|
8
|
+
get: vi.fn(),
|
|
9
|
+
post: vi.fn(),
|
|
10
|
+
patch: vi.fn(),
|
|
11
|
+
delete: vi.fn()
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const mockClient = {
|
|
15
|
+
getConfig: () => ({ apiKey: 'test-key', appSlug: 'test-app', apiUrl: 'https://api.test.com' }),
|
|
16
|
+
httpClient: mockHttpClient
|
|
17
|
+
} as unknown as Client
|
|
18
|
+
|
|
19
|
+
describe('Graph', () => {
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
vi.clearAllMocks()
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
describe('from()', () => {
|
|
25
|
+
it('returns new Graph instance', () => {
|
|
26
|
+
const graph = new Graph(mockClient)
|
|
27
|
+
expect(graph.from('employees')).toBeInstanceOf(Graph)
|
|
28
|
+
})
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
describe('traversal query params', () => {
|
|
32
|
+
it('include() sets include param', async () => {
|
|
33
|
+
mockHttpClient.get.mockResolvedValue([])
|
|
34
|
+
await new Graph(mockClient).from('employees').get('1').include('descendants').execute()
|
|
35
|
+
expect(mockHttpClient.get).toHaveBeenCalledWith(expect.stringContaining('include=descendants'))
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
it('include() supports ancestors', async () => {
|
|
39
|
+
mockHttpClient.get.mockResolvedValue([])
|
|
40
|
+
await new Graph(mockClient).from('employees').get('4').include('ancestors').execute()
|
|
41
|
+
expect(mockHttpClient.get).toHaveBeenCalledWith(expect.stringContaining('include=ancestors'))
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
it('include() supports both', async () => {
|
|
45
|
+
mockHttpClient.get.mockResolvedValue([])
|
|
46
|
+
await new Graph(mockClient).from('employees').get('2').include('both').execute()
|
|
47
|
+
expect(mockHttpClient.get).toHaveBeenCalledWith(expect.stringContaining('include=both'))
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
it('depth() sets depth param', async () => {
|
|
51
|
+
mockHttpClient.get.mockResolvedValue([])
|
|
52
|
+
await new Graph(mockClient).from('employees').get('1').include('descendants').depth(3).execute()
|
|
53
|
+
expect(mockHttpClient.get).toHaveBeenCalledWith(expect.stringContaining('depth=3'))
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
it('format() sets format param to tree', async () => {
|
|
57
|
+
mockHttpClient.get.mockResolvedValue([])
|
|
58
|
+
await new Graph(mockClient).from('employees').get('1').format('tree').depth(3).execute()
|
|
59
|
+
expect(mockHttpClient.get).toHaveBeenCalledWith(expect.stringContaining('format=tree'))
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
it('format() sets format param to graph', async () => {
|
|
63
|
+
mockHttpClient.get.mockResolvedValue([])
|
|
64
|
+
await new Graph(mockClient).from('employees').format('graph').execute()
|
|
65
|
+
expect(mockHttpClient.get).toHaveBeenCalledWith(expect.stringContaining('format=graph'))
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
it('types() sets relationship_type as repeated params', async () => {
|
|
69
|
+
mockHttpClient.get.mockResolvedValue([])
|
|
70
|
+
await new Graph(mockClient).from('employees').format('graph').types(['manager', 'dotted_line']).execute()
|
|
71
|
+
const url = mockHttpClient.get.mock.calls[0][0]
|
|
72
|
+
expect(url).toContain('relationship_type=manager')
|
|
73
|
+
expect(url).toContain('relationship_type=dotted_line')
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
it('types() with single type', async () => {
|
|
77
|
+
mockHttpClient.get.mockResolvedValue([])
|
|
78
|
+
await new Graph(mockClient).from('employees').format('graph').types(['manager']).execute()
|
|
79
|
+
expect(mockHttpClient.get).toHaveBeenCalledWith(expect.stringContaining('relationship_type=manager'))
|
|
80
|
+
})
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
describe('chaining', () => {
|
|
84
|
+
it('combines include, depth, and get', async () => {
|
|
85
|
+
mockHttpClient.get.mockResolvedValue([])
|
|
86
|
+
await new Graph(mockClient).from('employees').get('1').include('descendants').depth(3).execute()
|
|
87
|
+
const url = mockHttpClient.get.mock.calls[0][0]
|
|
88
|
+
expect(url).toContain('/1/')
|
|
89
|
+
expect(url).toContain('include=descendants')
|
|
90
|
+
expect(url).toContain('depth=3')
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
it('combines format, types, and depth', async () => {
|
|
94
|
+
mockHttpClient.get.mockResolvedValue([])
|
|
95
|
+
await new Graph(mockClient).from('employees').format('graph').types(['manager']).depth(2).execute()
|
|
96
|
+
const url = mockHttpClient.get.mock.calls[0][0]
|
|
97
|
+
expect(url).toContain('format=graph')
|
|
98
|
+
expect(url).toContain('relationship_type=manager')
|
|
99
|
+
expect(url).toContain('depth=2')
|
|
100
|
+
})
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
describe('URL building', () => {
|
|
104
|
+
it('builds correct base URL for data queries', async () => {
|
|
105
|
+
mockHttpClient.get.mockResolvedValue([])
|
|
106
|
+
await new Graph(mockClient).from('employees').execute()
|
|
107
|
+
expect(mockHttpClient.get).toHaveBeenCalledWith('api/apps/test-app/datatables/employees/data/')
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
it('builds correct URL with record ID', async () => {
|
|
111
|
+
mockHttpClient.get.mockResolvedValue({})
|
|
112
|
+
await new Graph(mockClient).from('employees').get('1').execute()
|
|
113
|
+
expect(mockHttpClient.get).toHaveBeenCalledWith('api/apps/test-app/datatables/employees/data/1/')
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
it('appends query string', async () => {
|
|
117
|
+
mockHttpClient.get.mockResolvedValue([])
|
|
118
|
+
await new Graph(mockClient).from('employees').get('1').include('descendants').depth(2).execute()
|
|
119
|
+
const url = mockHttpClient.get.mock.calls[0][0]
|
|
120
|
+
expect(url).toContain('api/apps/test-app/datatables/employees/data/1/')
|
|
121
|
+
expect(url).toContain('?')
|
|
122
|
+
})
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
describe('edge CRUD', () => {
|
|
126
|
+
it('listEdges() calls GET on edges route', async () => {
|
|
127
|
+
mockHttpClient.get.mockResolvedValue({ edges: [], total: 0 })
|
|
128
|
+
await new Graph(mockClient).from('employees').listEdges().execute()
|
|
129
|
+
expect(mockHttpClient.get).toHaveBeenCalledWith('api/apps/test-app/datatables/employees/edges/')
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
it('createEdge() calls POST with array of edges', async () => {
|
|
133
|
+
const edges = [
|
|
134
|
+
{ from: 5, to: 2, type: 'manager' },
|
|
135
|
+
{ from: 5, to: 3, type: 'dotted_line', metadata: { project: 'AI' } }
|
|
136
|
+
]
|
|
137
|
+
mockHttpClient.post.mockResolvedValue({ status: 'success', data: edges, total: 2 })
|
|
138
|
+
await new Graph(mockClient).from('employees').createEdge(edges).execute()
|
|
139
|
+
expect(mockHttpClient.post).toHaveBeenCalledWith(
|
|
140
|
+
'api/apps/test-app/datatables/employees/edges/',
|
|
141
|
+
edges
|
|
142
|
+
)
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
it('createEdge() with metadata', async () => {
|
|
146
|
+
const edges = [{ from: 5, to: 3, type: 'dotted_line', metadata: { percentage: 30 } }]
|
|
147
|
+
mockHttpClient.post.mockResolvedValue({ status: 'success', data: edges, total: 1 })
|
|
148
|
+
await new Graph(mockClient).from('employees').createEdge(edges).execute()
|
|
149
|
+
expect(mockHttpClient.post).toHaveBeenCalledWith(
|
|
150
|
+
'api/apps/test-app/datatables/employees/edges/',
|
|
151
|
+
edges
|
|
152
|
+
)
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
it('updateEdge() calls PATCH with edge ID in URL', async () => {
|
|
156
|
+
const edge = { from: 5, to: 3, type: 'dotted_line' }
|
|
157
|
+
mockHttpClient.patch.mockResolvedValue({ id: 9, ...edge })
|
|
158
|
+
await new Graph(mockClient).from('employees').updateEdge('9', edge).execute()
|
|
159
|
+
expect(mockHttpClient.patch).toHaveBeenCalledWith(
|
|
160
|
+
'api/apps/test-app/datatables/employees/edges/9/',
|
|
161
|
+
edge
|
|
162
|
+
)
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
it('deleteEdge() calls DELETE with edge_ids object', async () => {
|
|
166
|
+
mockHttpClient.delete.mockResolvedValue({ deleted: 2 })
|
|
167
|
+
await new Graph(mockClient).from('employees').deleteEdge([9, 10]).execute()
|
|
168
|
+
expect(mockHttpClient.delete).toHaveBeenCalledWith(
|
|
169
|
+
'api/apps/test-app/datatables/employees/edges/',
|
|
170
|
+
{ edge_ids: [9, 10] }
|
|
171
|
+
)
|
|
172
|
+
})
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
describe('execute()', () => {
|
|
176
|
+
it('defaults to GET for traversal queries', async () => {
|
|
177
|
+
mockHttpClient.get.mockResolvedValue([])
|
|
178
|
+
await new Graph(mockClient).from('employees').execute()
|
|
179
|
+
expect(mockHttpClient.get).toHaveBeenCalled()
|
|
180
|
+
expect(mockHttpClient.post).not.toHaveBeenCalled()
|
|
181
|
+
})
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
describe('response handling', () => {
|
|
185
|
+
it('returns descendants response with base record and related records', async () => {
|
|
186
|
+
const mockResponse = {
|
|
187
|
+
status: 'success',
|
|
188
|
+
message: 'Data retrieved successfully',
|
|
189
|
+
data: {
|
|
190
|
+
data: { id: 1, name: 'Alice Chen', title: 'CEO' },
|
|
191
|
+
reports: [
|
|
192
|
+
{ id: 2, name: 'Bob Smith', _depth: 1, _relationship_type: 'manager' },
|
|
193
|
+
{ id: 3, name: 'Carol White', _depth: 1, _relationship_type: 'manager' }
|
|
194
|
+
]
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
mockHttpClient.get.mockResolvedValue(mockResponse)
|
|
198
|
+
const result = await new Graph(mockClient).from('employees').get('1').include('descendants').execute()
|
|
199
|
+
expect(result).toEqual(mockResponse)
|
|
200
|
+
expect((result as any).data.data.id).toBe(1)
|
|
201
|
+
expect((result as any).data.reports).toHaveLength(2)
|
|
202
|
+
expect((result as any).data.reports[0]._depth).toBe(1)
|
|
203
|
+
expect((result as any).data.reports[0]._relationship_type).toBe('manager')
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
it('returns ancestors response with manager chain', async () => {
|
|
207
|
+
const mockResponse = {
|
|
208
|
+
status: 'success',
|
|
209
|
+
message: 'Data retrieved successfully',
|
|
210
|
+
data: {
|
|
211
|
+
data: { id: 4, name: 'David Lee' },
|
|
212
|
+
manager: [
|
|
213
|
+
{ id: 2, name: 'Bob Smith', _depth: 1, _relationship_type: 'manager' },
|
|
214
|
+
{ id: 1, name: 'Alice Chen', _depth: 2, _relationship_type: 'manager' }
|
|
215
|
+
]
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
mockHttpClient.get.mockResolvedValue(mockResponse)
|
|
219
|
+
const result = await new Graph(mockClient).from('employees').get('4').include('ancestors').execute()
|
|
220
|
+
expect((result as any).data.manager).toHaveLength(2)
|
|
221
|
+
expect((result as any).data.manager[1]._depth).toBe(2)
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
it('returns both directions response', async () => {
|
|
225
|
+
const mockResponse = {
|
|
226
|
+
status: 'success',
|
|
227
|
+
message: 'Data retrieved successfully',
|
|
228
|
+
data: {
|
|
229
|
+
data: { id: 2, name: 'Bob Smith' },
|
|
230
|
+
manager: [{ id: 1, name: 'Alice Chen', _depth: 1, _relationship_type: 'manager' }],
|
|
231
|
+
reports: [{ id: 4, name: 'David Lee', _depth: 1, _relationship_type: 'manager' }]
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
mockHttpClient.get.mockResolvedValue(mockResponse)
|
|
235
|
+
const result = await new Graph(mockClient).from('employees').get('2').include('both').depth(1).execute()
|
|
236
|
+
expect((result as any).data.manager).toHaveLength(1)
|
|
237
|
+
expect((result as any).data.reports).toHaveLength(1)
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
it('returns tree format with nested children', async () => {
|
|
241
|
+
const mockResponse = {
|
|
242
|
+
status: 'success',
|
|
243
|
+
message: 'Tree data retrieved successfully',
|
|
244
|
+
data: [{
|
|
245
|
+
id: 1,
|
|
246
|
+
name: 'Alice Chen',
|
|
247
|
+
_depth: 0,
|
|
248
|
+
children: [
|
|
249
|
+
{ id: 2, name: 'Bob Smith', _depth: 1, _relationship_type: 'manager', children: [] },
|
|
250
|
+
{ id: 3, name: 'Carol White', _depth: 1, _relationship_type: 'manager', children: [] }
|
|
251
|
+
]
|
|
252
|
+
}],
|
|
253
|
+
total: 3
|
|
254
|
+
}
|
|
255
|
+
mockHttpClient.get.mockResolvedValue(mockResponse)
|
|
256
|
+
const result = await new Graph(mockClient).from('employees').get('1').format('tree').depth(2).execute()
|
|
257
|
+
expect((result as any).data[0].children).toHaveLength(2)
|
|
258
|
+
expect((result as any).data[0].children[0]._relationship_type).toBe('manager')
|
|
259
|
+
expect((result as any).total).toBe(3)
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
it('returns graph format with nodes and edges', async () => {
|
|
263
|
+
const mockResponse = {
|
|
264
|
+
status: 'success',
|
|
265
|
+
message: 'Graph data retrieved successfully',
|
|
266
|
+
data: {
|
|
267
|
+
nodes: [
|
|
268
|
+
{ id: 1, name: 'Alice Chen', title: 'CEO' },
|
|
269
|
+
{ id: 2, name: 'Bob Smith', title: 'VP Engineering' }
|
|
270
|
+
],
|
|
271
|
+
edges: [
|
|
272
|
+
{ id: 9, from: 2, to: 1, type: 'manager', metadata: { primary: true } }
|
|
273
|
+
]
|
|
274
|
+
},
|
|
275
|
+
total: 2
|
|
276
|
+
}
|
|
277
|
+
mockHttpClient.get.mockResolvedValue(mockResponse)
|
|
278
|
+
const result = await new Graph(mockClient).from('employees').format('graph').types(['manager']).execute()
|
|
279
|
+
expect((result as any).data.nodes).toHaveLength(2)
|
|
280
|
+
expect((result as any).data.edges).toHaveLength(1)
|
|
281
|
+
expect((result as any).data.edges[0].type).toBe('manager')
|
|
282
|
+
})
|
|
283
|
+
|
|
284
|
+
it('returns edge list response', async () => {
|
|
285
|
+
const mockResponse = {
|
|
286
|
+
edges: [
|
|
287
|
+
{ id: 1, from: 5, to: 2, type: 'manager', metadata: {} },
|
|
288
|
+
{ id: 2, from: 5, to: 3, type: 'dotted_line', metadata: { percentage: 30 } }
|
|
289
|
+
],
|
|
290
|
+
total: 2
|
|
291
|
+
}
|
|
292
|
+
mockHttpClient.get.mockResolvedValue(mockResponse)
|
|
293
|
+
const result = await new Graph(mockClient).from('employees').listEdges().execute()
|
|
294
|
+
expect((result as any).edges).toHaveLength(2)
|
|
295
|
+
expect((result as any).edges[0].type).toBe('manager')
|
|
296
|
+
expect((result as any).total).toBe(2)
|
|
297
|
+
})
|
|
298
|
+
|
|
299
|
+
it('returns created edges response matching TaruviResponse<EdgeResponse[]>', async () => {
|
|
300
|
+
const mockResponse: TaruviResponse<EdgeResponse[]> = {
|
|
301
|
+
status: 'success',
|
|
302
|
+
message: 'Edges created successfully',
|
|
303
|
+
data: [
|
|
304
|
+
{ id: 10, from: 5, to: 2, type: 'manager', metadata: {} }
|
|
305
|
+
],
|
|
306
|
+
total: 1
|
|
307
|
+
}
|
|
308
|
+
mockHttpClient.post.mockResolvedValue(mockResponse)
|
|
309
|
+
const result = await new Graph(mockClient).from('employees').createEdge([{ from: 5, to: 2, type: 'manager' }]).execute() as TaruviResponse<EdgeResponse[]>
|
|
310
|
+
expect(result.data).toHaveLength(1)
|
|
311
|
+
expect(result.data[0].id).toBe(10)
|
|
312
|
+
})
|
|
313
|
+
|
|
314
|
+
it('returns updated edge matching EdgeResponse type', async () => {
|
|
315
|
+
const mockResponse: EdgeResponse = { id: 9, from: 5, to: 3, type: 'dotted_line', metadata: {} }
|
|
316
|
+
mockHttpClient.patch.mockResolvedValue(mockResponse)
|
|
317
|
+
const result = await new Graph(mockClient).from('employees').updateEdge('9', { from: 5, to: 3, type: 'dotted_line' }).execute() as EdgeResponse
|
|
318
|
+
expect(result.id).toBe(9)
|
|
319
|
+
expect(result.type).toBe('dotted_line')
|
|
320
|
+
})
|
|
321
|
+
|
|
322
|
+
it('returns delete count response', async () => {
|
|
323
|
+
const mockResponse = { deleted: 3 }
|
|
324
|
+
mockHttpClient.delete.mockResolvedValue(mockResponse)
|
|
325
|
+
const result = await new Graph(mockClient).from('employees').deleteEdge([1, 2, 3]).execute()
|
|
326
|
+
expect((result as any).deleted).toBe(3)
|
|
327
|
+
})
|
|
328
|
+
})
|
|
329
|
+
})
|