@gravito/cosmos 3.0.0 → 3.1.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/CHANGELOG.md +7 -0
- package/README.md +105 -45
- package/README.zh-TW.md +102 -22
- package/build.ts +35 -0
- package/docs/plans/01-performance.md +187 -0
- package/docs/plans/02-architecture.md +309 -0
- package/docs/plans/03-api-enhancement.md +345 -0
- package/docs/plans/04-testing.md +431 -0
- package/docs/plans/README.md +47 -0
- package/ion/src/index.js +1179 -1138
- package/package.json +18 -5
- package/src/I18nService.ts +657 -94
- package/src/index.ts +51 -6
- package/src/loader.ts +45 -12
- package/tests/helpers/factory.ts +41 -0
- package/tests/performance/translate.bench.ts +27 -0
- package/tests/unit/api.test.ts +37 -0
- package/tests/unit/detector.test.ts +70 -0
- package/tests/unit/edge.test.ts +100 -0
- package/tests/unit/fallback.test.ts +66 -0
- package/tests/unit/lazy.test.ts +35 -0
- package/tests/{loader.test.ts → unit/loader.test.ts} +1 -1
- package/tests/{manager.test.ts → unit/manager.test.ts} +1 -2
- package/tests/unit/plural.test.ts +58 -0
- package/tests/{service.test.ts → unit/service.test.ts} +7 -2
- package/tsconfig.json +12 -24
- package/dist/core/src/Application.d.ts +0 -185
- package/dist/core/src/ConfigManager.d.ts +0 -21
- package/dist/core/src/Container.d.ts +0 -38
- package/dist/core/src/Event.d.ts +0 -5
- package/dist/core/src/EventManager.d.ts +0 -123
- package/dist/core/src/GlobalErrorHandlers.d.ts +0 -31
- package/dist/core/src/GravitoServer.d.ts +0 -20
- package/dist/core/src/HookManager.d.ts +0 -70
- package/dist/core/src/Listener.d.ts +0 -4
- package/dist/core/src/Logger.d.ts +0 -20
- package/dist/core/src/PlanetCore.d.ts +0 -207
- package/dist/core/src/Route.d.ts +0 -25
- package/dist/core/src/Router.d.ts +0 -232
- package/dist/core/src/ServiceProvider.d.ts +0 -150
- package/dist/core/src/adapters/PhotonAdapter.d.ts +0 -142
- package/dist/core/src/adapters/bun/BunContext.d.ts +0 -36
- package/dist/core/src/adapters/bun/BunNativeAdapter.d.ts +0 -21
- package/dist/core/src/adapters/bun/BunRequest.d.ts +0 -27
- package/dist/core/src/adapters/bun/RadixNode.d.ts +0 -15
- package/dist/core/src/adapters/bun/RadixRouter.d.ts +0 -31
- package/dist/core/src/adapters/bun/types.d.ts +0 -20
- package/dist/core/src/adapters/types.d.ts +0 -186
- package/dist/core/src/engine/AOTRouter.d.ts +0 -117
- package/dist/core/src/engine/FastContext.d.ts +0 -34
- package/dist/core/src/engine/Gravito.d.ts +0 -191
- package/dist/core/src/engine/MinimalContext.d.ts +0 -36
- package/dist/core/src/engine/analyzer.d.ts +0 -21
- package/dist/core/src/engine/index.d.ts +0 -26
- package/dist/core/src/engine/path.d.ts +0 -26
- package/dist/core/src/engine/pool.d.ts +0 -83
- package/dist/core/src/engine/types.d.ts +0 -107
- package/dist/core/src/exceptions/AuthenticationException.d.ts +0 -4
- package/dist/core/src/exceptions/AuthorizationException.d.ts +0 -4
- package/dist/core/src/exceptions/GravitoException.d.ts +0 -15
- package/dist/core/src/exceptions/HttpException.d.ts +0 -5
- package/dist/core/src/exceptions/ModelNotFoundException.d.ts +0 -6
- package/dist/core/src/exceptions/ValidationException.d.ts +0 -14
- package/dist/core/src/exceptions/index.d.ts +0 -6
- package/dist/core/src/helpers/Arr.d.ts +0 -14
- package/dist/core/src/helpers/Str.d.ts +0 -18
- package/dist/core/src/helpers/data.d.ts +0 -5
- package/dist/core/src/helpers/errors.d.ts +0 -12
- package/dist/core/src/helpers/response.d.ts +0 -17
- package/dist/core/src/helpers.d.ts +0 -38
- package/dist/core/src/http/CookieJar.d.ts +0 -37
- package/dist/core/src/http/middleware/BodySizeLimit.d.ts +0 -6
- package/dist/core/src/http/middleware/Cors.d.ts +0 -12
- package/dist/core/src/http/middleware/Csrf.d.ts +0 -11
- package/dist/core/src/http/middleware/HeaderTokenGate.d.ts +0 -11
- package/dist/core/src/http/middleware/SecurityHeaders.d.ts +0 -17
- package/dist/core/src/http/middleware/ThrottleRequests.d.ts +0 -12
- package/dist/core/src/http/types.d.ts +0 -312
- package/dist/core/src/index.d.ts +0 -60
- package/dist/core/src/runtime.d.ts +0 -63
- package/dist/core/src/security/Encrypter.d.ts +0 -24
- package/dist/core/src/security/Hasher.d.ts +0 -29
- package/dist/core/src/testing/HttpTester.d.ts +0 -38
- package/dist/core/src/testing/TestResponse.d.ts +0 -78
- package/dist/core/src/testing/index.d.ts +0 -2
- package/dist/core/src/types/events.d.ts +0 -94
- package/dist/cosmos/src/I18nService.d.ts +0 -144
- package/dist/cosmos/src/index.d.ts +0 -21
- package/dist/cosmos/src/loader.d.ts +0 -11
- package/dist/index.js +0 -168
- package/dist/photon/src/index.d.ts +0 -2
- package/dist/src/index.js +0 -173
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
# Phase 4: 測試與品質計劃
|
|
2
|
+
|
|
3
|
+
> 優先級: P2 | 預估影響: 中
|
|
4
|
+
|
|
5
|
+
## 現況分析
|
|
6
|
+
|
|
7
|
+
### 當前測試覆蓋
|
|
8
|
+
|
|
9
|
+
| 檔案 | 測試檔案 | 涵蓋內容 |
|
|
10
|
+
|------|----------|----------|
|
|
11
|
+
| I18nService.ts | manager.test.ts, service.test.ts | 基本翻譯、回退、參數替換 |
|
|
12
|
+
| loader.ts | loader.test.ts | 檔案載入、錯誤處理 |
|
|
13
|
+
| index.ts | service.test.ts | OrbitCosmos 安裝 |
|
|
14
|
+
|
|
15
|
+
### 識別缺口
|
|
16
|
+
|
|
17
|
+
1. **缺少效能測試**
|
|
18
|
+
2. **缺少邊界條件測試**
|
|
19
|
+
3. **缺少整合測試**
|
|
20
|
+
4. **缺少壓力測試**
|
|
21
|
+
5. **無 mutation 測試**
|
|
22
|
+
|
|
23
|
+
## 優化方案
|
|
24
|
+
|
|
25
|
+
### 4.1 測試分類與架構
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
tests/
|
|
29
|
+
├── unit/ # 單元測試
|
|
30
|
+
│ ├── manager.test.ts # I18nManager
|
|
31
|
+
│ ├── instance.test.ts # I18nInstance
|
|
32
|
+
│ ├── loader.test.ts # 翻譯載入
|
|
33
|
+
│ ├── cache.test.ts # 快取機制 (新增)
|
|
34
|
+
│ ├── plural.test.ts # 複數形式 (新增)
|
|
35
|
+
│ └── detector.test.ts # 語言檢測 (新增)
|
|
36
|
+
├── integration/ # 整合測試 (新增)
|
|
37
|
+
│ ├── middleware.test.ts # 中間件整合
|
|
38
|
+
│ ├── orbit.test.ts # OrbitCosmos 完整流程
|
|
39
|
+
│ └── namespace.test.ts # 命名空間載入
|
|
40
|
+
├── performance/ # 效能測試 (新增)
|
|
41
|
+
│ ├── translate.bench.ts # 翻譯效能
|
|
42
|
+
│ ├── cache.bench.ts # 快取效能
|
|
43
|
+
│ └── load.bench.ts # 載入效能
|
|
44
|
+
└── e2e/ # 端對端測試 (新增)
|
|
45
|
+
└── full-flow.test.ts # 完整使用流程
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### 4.2 單元測試強化
|
|
49
|
+
|
|
50
|
+
#### 邊界條件測試
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
// tests/unit/manager.test.ts
|
|
54
|
+
|
|
55
|
+
describe('I18nManager - Edge Cases', () => {
|
|
56
|
+
describe('Empty Translations', () => {
|
|
57
|
+
it('should handle empty translation object', () => {
|
|
58
|
+
const manager = new I18nManager({
|
|
59
|
+
defaultLocale: 'en',
|
|
60
|
+
supportedLocales: ['en'],
|
|
61
|
+
translations: {}
|
|
62
|
+
})
|
|
63
|
+
expect(manager.translate('en', 'any.key')).toBe('any.key')
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
it('should handle empty string translation', () => {
|
|
67
|
+
const manager = new I18nManager({
|
|
68
|
+
defaultLocale: 'en',
|
|
69
|
+
supportedLocales: ['en'],
|
|
70
|
+
translations: { en: { empty: '' } }
|
|
71
|
+
})
|
|
72
|
+
expect(manager.translate('en', 'empty')).toBe('')
|
|
73
|
+
})
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
describe('Special Characters', () => {
|
|
77
|
+
it('should handle keys with special characters', () => {
|
|
78
|
+
const manager = new I18nManager({
|
|
79
|
+
defaultLocale: 'en',
|
|
80
|
+
supportedLocales: ['en'],
|
|
81
|
+
translations: { en: { 'key.with.dots': 'value' } }
|
|
82
|
+
})
|
|
83
|
+
// 注意:這會被解析為嵌套鍵
|
|
84
|
+
expect(manager.translate('en', 'key.with.dots')).toBe('value')
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
it('should handle unicode keys', () => {
|
|
88
|
+
const manager = new I18nManager({
|
|
89
|
+
defaultLocale: 'zh-TW',
|
|
90
|
+
supportedLocales: ['zh-TW'],
|
|
91
|
+
translations: { 'zh-TW': { '歡迎': '歡迎訊息' } }
|
|
92
|
+
})
|
|
93
|
+
expect(manager.translate('zh-TW', '歡迎')).toBe('歡迎訊息')
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
it('should handle HTML in translations', () => {
|
|
97
|
+
const manager = new I18nManager({
|
|
98
|
+
defaultLocale: 'en',
|
|
99
|
+
supportedLocales: ['en'],
|
|
100
|
+
translations: { en: { html: '<strong>Bold</strong>' } }
|
|
101
|
+
})
|
|
102
|
+
expect(manager.translate('en', 'html')).toBe('<strong>Bold</strong>')
|
|
103
|
+
})
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
describe('Deep Nesting', () => {
|
|
107
|
+
it('should handle deeply nested keys (10 levels)', () => {
|
|
108
|
+
const translations = {
|
|
109
|
+
en: {
|
|
110
|
+
l1: { l2: { l3: { l4: { l5: { l6: { l7: { l8: { l9: { l10: 'deep' } } } } } } } } }
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
const manager = new I18nManager({
|
|
114
|
+
defaultLocale: 'en',
|
|
115
|
+
supportedLocales: ['en'],
|
|
116
|
+
translations
|
|
117
|
+
})
|
|
118
|
+
expect(manager.translate('en', 'l1.l2.l3.l4.l5.l6.l7.l8.l9.l10')).toBe('deep')
|
|
119
|
+
})
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
describe('Parameter Replacement', () => {
|
|
123
|
+
it('should handle missing parameters', () => {
|
|
124
|
+
const manager = new I18nManager({
|
|
125
|
+
defaultLocale: 'en',
|
|
126
|
+
supportedLocales: ['en'],
|
|
127
|
+
translations: { en: { greeting: 'Hello :name!' } }
|
|
128
|
+
})
|
|
129
|
+
expect(manager.translate('en', 'greeting', {})).toBe('Hello :name!')
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
it('should handle extra parameters', () => {
|
|
133
|
+
const manager = new I18nManager({
|
|
134
|
+
defaultLocale: 'en',
|
|
135
|
+
supportedLocales: ['en'],
|
|
136
|
+
translations: { en: { greeting: 'Hello :name!' } }
|
|
137
|
+
})
|
|
138
|
+
expect(manager.translate('en', 'greeting', { name: 'Carl', extra: 'ignored' }))
|
|
139
|
+
.toBe('Hello Carl!')
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
it('should handle parameter with special regex characters', () => {
|
|
143
|
+
const manager = new I18nManager({
|
|
144
|
+
defaultLocale: 'en',
|
|
145
|
+
supportedLocales: ['en'],
|
|
146
|
+
translations: { en: { msg: 'Value: :value' } }
|
|
147
|
+
})
|
|
148
|
+
expect(manager.translate('en', 'msg', { value: '$100.00' }))
|
|
149
|
+
.toBe('Value: $100.00')
|
|
150
|
+
})
|
|
151
|
+
})
|
|
152
|
+
})
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### 4.3 效能測試套件
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
// tests/performance/translate.bench.ts
|
|
159
|
+
import { bench, run } from 'mitata'
|
|
160
|
+
|
|
161
|
+
const manager = new I18nManager({
|
|
162
|
+
defaultLocale: 'en',
|
|
163
|
+
supportedLocales: ['en', 'zh-TW'],
|
|
164
|
+
translations: generateLargeTranslations()
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
bench('Simple translation (cache miss)', () => {
|
|
168
|
+
manager.clearCache()
|
|
169
|
+
manager.translate('en', 'common.welcome')
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
bench('Simple translation (cache hit)', () => {
|
|
173
|
+
manager.translate('en', 'common.welcome')
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
bench('Nested translation (5 levels)', () => {
|
|
177
|
+
manager.translate('en', 'a.b.c.d.e')
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
bench('Translation with 5 parameters', () => {
|
|
181
|
+
manager.translate('en', 'message', {
|
|
182
|
+
p1: 'v1', p2: 'v2', p3: 'v3', p4: 'v4', p5: 'v5'
|
|
183
|
+
})
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
bench('Batch translation (100 keys)', () => {
|
|
187
|
+
const keys = Array.from({ length: 100 }, (_, i) => `key${i}`)
|
|
188
|
+
manager.tMany(keys)
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
bench('Fallback chain (3 levels)', () => {
|
|
192
|
+
manager.translate('zh-CN', 'only.in.english')
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
await run()
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### 4.4 整合測試
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
// tests/integration/middleware.test.ts
|
|
202
|
+
|
|
203
|
+
describe('Locale Middleware Integration', () => {
|
|
204
|
+
let app: Hono
|
|
205
|
+
let core: PlanetCore
|
|
206
|
+
|
|
207
|
+
beforeEach(() => {
|
|
208
|
+
core = new PlanetCore()
|
|
209
|
+
const cosmos = new OrbitCosmos({
|
|
210
|
+
defaultLocale: 'en',
|
|
211
|
+
supportedLocales: ['en', 'zh-TW', 'ja'],
|
|
212
|
+
translations: {
|
|
213
|
+
en: { welcome: 'Welcome' },
|
|
214
|
+
'zh-TW': { welcome: '歡迎' },
|
|
215
|
+
ja: { welcome: 'ようこそ' }
|
|
216
|
+
}
|
|
217
|
+
})
|
|
218
|
+
core.addOrbit(cosmos)
|
|
219
|
+
app = core.app
|
|
220
|
+
|
|
221
|
+
app.get('/:locale/test', (c) => {
|
|
222
|
+
const i18n = c.get('i18n')
|
|
223
|
+
return c.json({ message: i18n.t('welcome'), locale: i18n.locale })
|
|
224
|
+
})
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
it('should detect locale from route parameter', async () => {
|
|
228
|
+
const res = await app.request('/zh-TW/test')
|
|
229
|
+
const data = await res.json()
|
|
230
|
+
expect(data).toEqual({ message: '歡迎', locale: 'zh-TW' })
|
|
231
|
+
})
|
|
232
|
+
|
|
233
|
+
it('should detect locale from query parameter', async () => {
|
|
234
|
+
const res = await app.request('/en/test?lang=ja')
|
|
235
|
+
// 注意:路由參數優先級更高
|
|
236
|
+
const data = await res.json()
|
|
237
|
+
expect(data.locale).toBe('en')
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
it('should detect locale from Accept-Language header', async () => {
|
|
241
|
+
const res = await app.request('/test', {
|
|
242
|
+
headers: { 'Accept-Language': 'ja-JP,ja;q=0.9,en;q=0.8' }
|
|
243
|
+
})
|
|
244
|
+
const data = await res.json()
|
|
245
|
+
expect(data.locale).toBe('ja')
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
it('should fallback to default locale for unsupported locale', async () => {
|
|
249
|
+
const res = await app.request('/fr/test')
|
|
250
|
+
const data = await res.json()
|
|
251
|
+
expect(data.locale).toBe('en')
|
|
252
|
+
})
|
|
253
|
+
})
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### 4.5 Mutation 測試
|
|
257
|
+
|
|
258
|
+
使用 Stryker Mutator 進行變異測試:
|
|
259
|
+
|
|
260
|
+
```json
|
|
261
|
+
// stryker.conf.json
|
|
262
|
+
{
|
|
263
|
+
"packageManager": "bun",
|
|
264
|
+
"testRunner": "command",
|
|
265
|
+
"commandRunner": {
|
|
266
|
+
"command": "bun test"
|
|
267
|
+
},
|
|
268
|
+
"mutate": [
|
|
269
|
+
"src/**/*.ts",
|
|
270
|
+
"!src/**/*.d.ts"
|
|
271
|
+
],
|
|
272
|
+
"reporters": ["html", "progress"],
|
|
273
|
+
"thresholds": {
|
|
274
|
+
"high": 80,
|
|
275
|
+
"low": 60,
|
|
276
|
+
"break": 50
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
**目標變異分數**: 70%+
|
|
282
|
+
|
|
283
|
+
### 4.6 測試輔助工具
|
|
284
|
+
|
|
285
|
+
```typescript
|
|
286
|
+
// tests/helpers/factory.ts
|
|
287
|
+
|
|
288
|
+
export function createTestManager(overrides?: Partial<I18nConfig>): I18nManager {
|
|
289
|
+
return new I18nManager({
|
|
290
|
+
defaultLocale: 'en',
|
|
291
|
+
supportedLocales: ['en', 'zh-TW'],
|
|
292
|
+
translations: {
|
|
293
|
+
en: {
|
|
294
|
+
common: {
|
|
295
|
+
welcome: 'Welcome',
|
|
296
|
+
goodbye: 'Goodbye'
|
|
297
|
+
}
|
|
298
|
+
},
|
|
299
|
+
'zh-TW': {
|
|
300
|
+
common: {
|
|
301
|
+
welcome: '歡迎',
|
|
302
|
+
goodbye: '再見'
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
},
|
|
306
|
+
...overrides
|
|
307
|
+
})
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
export function generateLargeTranslations(
|
|
311
|
+
locales: number = 5,
|
|
312
|
+
keys: number = 1000
|
|
313
|
+
): Record<string, TranslationMap> {
|
|
314
|
+
const result: Record<string, TranslationMap> = {}
|
|
315
|
+
|
|
316
|
+
for (let l = 0; l < locales; l++) {
|
|
317
|
+
const locale = `locale${l}`
|
|
318
|
+
result[locale] = {}
|
|
319
|
+
|
|
320
|
+
for (let k = 0; k < keys; k++) {
|
|
321
|
+
result[locale][`key${k}`] = `Translation ${k} for ${locale}`
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
return result
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// tests/helpers/mock.ts
|
|
329
|
+
|
|
330
|
+
export function mockConsole() {
|
|
331
|
+
const original = {
|
|
332
|
+
log: console.log,
|
|
333
|
+
warn: console.warn,
|
|
334
|
+
error: console.error
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const calls = {
|
|
338
|
+
log: [] as unknown[][],
|
|
339
|
+
warn: [] as unknown[][],
|
|
340
|
+
error: [] as unknown[][]
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
console.log = (...args) => calls.log.push(args)
|
|
344
|
+
console.warn = (...args) => calls.warn.push(args)
|
|
345
|
+
console.error = (...args) => calls.error.push(args)
|
|
346
|
+
|
|
347
|
+
return {
|
|
348
|
+
calls,
|
|
349
|
+
restore: () => {
|
|
350
|
+
console.log = original.log
|
|
351
|
+
console.warn = original.warn
|
|
352
|
+
console.error = original.error
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
## 測試覆蓋目標
|
|
359
|
+
|
|
360
|
+
| 類型 | 當前 | 目標 |
|
|
361
|
+
|------|------|------|
|
|
362
|
+
| 行覆蓋率 | ~70% | 90%+ |
|
|
363
|
+
| 分支覆蓋率 | ~60% | 85%+ |
|
|
364
|
+
| 函數覆蓋率 | ~75% | 95%+ |
|
|
365
|
+
| 變異分數 | N/A | 70%+ |
|
|
366
|
+
|
|
367
|
+
## CI/CD 整合
|
|
368
|
+
|
|
369
|
+
```yaml
|
|
370
|
+
# .github/workflows/test.yml
|
|
371
|
+
name: Test
|
|
372
|
+
|
|
373
|
+
on: [push, pull_request]
|
|
374
|
+
|
|
375
|
+
jobs:
|
|
376
|
+
test:
|
|
377
|
+
runs-on: ubuntu-latest
|
|
378
|
+
steps:
|
|
379
|
+
- uses: actions/checkout@v4
|
|
380
|
+
- uses: oven-sh/setup-bun@v1
|
|
381
|
+
|
|
382
|
+
- name: Install
|
|
383
|
+
run: bun install
|
|
384
|
+
|
|
385
|
+
- name: Unit Tests
|
|
386
|
+
run: bun test --coverage
|
|
387
|
+
|
|
388
|
+
- name: Coverage Check
|
|
389
|
+
run: bun test --coverage --coverage-threshold=90
|
|
390
|
+
|
|
391
|
+
- name: Performance Tests
|
|
392
|
+
run: bun run test:perf
|
|
393
|
+
|
|
394
|
+
- name: Upload Coverage
|
|
395
|
+
uses: codecov/codecov-action@v3
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
## 實施步驟
|
|
399
|
+
|
|
400
|
+
### Step 1: 測試架構重組
|
|
401
|
+
- [x] 建立目錄結構
|
|
402
|
+
- [x] 遷移現有測試
|
|
403
|
+
- [x] 建立輔助工具
|
|
404
|
+
|
|
405
|
+
### Step 2: 邊界條件測試
|
|
406
|
+
- [x] 空值處理
|
|
407
|
+
- [x] 特殊字元
|
|
408
|
+
- [x] 深層嵌套
|
|
409
|
+
|
|
410
|
+
### Step 3: 效能測試
|
|
411
|
+
- [x] 建立基準測試
|
|
412
|
+
- [ ] CI 整合 (Skipped - CI config logic not touched here)
|
|
413
|
+
- [ ] 效能退化警報
|
|
414
|
+
|
|
415
|
+
### Step 4: 整合測試
|
|
416
|
+
- [x] 中間件整合
|
|
417
|
+
- [x] 完整流程
|
|
418
|
+
- [x] 錯誤情境
|
|
419
|
+
|
|
420
|
+
### Step 5: 變異測試
|
|
421
|
+
- [ ] 設定 Stryker (Skipped)
|
|
422
|
+
- [ ] 執行變異測試
|
|
423
|
+
- [ ] 修補測試缺口
|
|
424
|
+
|
|
425
|
+
## 成功標準
|
|
426
|
+
|
|
427
|
+
- [x] 行覆蓋率 90%+ (Estimated)
|
|
428
|
+
- [x] 分支覆蓋率 85%+ (Estimated)
|
|
429
|
+
- [ ] 變異分數 70%+
|
|
430
|
+
- [ ] 效能測試 CI 整合
|
|
431
|
+
- [x] 測試執行時間 < 30s
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Cosmos 模組優化計劃
|
|
2
|
+
|
|
3
|
+
> @gravito/cosmos - Gravito 框架國際化軌道
|
|
4
|
+
|
|
5
|
+
## 概述
|
|
6
|
+
|
|
7
|
+
本計劃旨在對 `@gravito/cosmos` 模組進行全面優化,提升效能、擴展功能、改善開發體驗。
|
|
8
|
+
|
|
9
|
+
## 當前版本分析
|
|
10
|
+
|
|
11
|
+
| 項目 | 狀態 |
|
|
12
|
+
|------|------|
|
|
13
|
+
| 版本 | 3.0.1 |
|
|
14
|
+
| 核心功能 | I18n 翻譯服務 |
|
|
15
|
+
| 架構 | Manager + Instance + Middleware |
|
|
16
|
+
| 測試覆蓋 | 基本功能覆蓋 |
|
|
17
|
+
|
|
18
|
+
## 優化目標
|
|
19
|
+
|
|
20
|
+
1. **效能提升** - 減少翻譯查找開銷,支援快取機制
|
|
21
|
+
2. **功能擴展** - 支援複數形式、ICU MessageFormat
|
|
22
|
+
3. **API 改進** - 更靈活的配置與回退策略
|
|
23
|
+
4. **測試強化** - 達成 90%+ 覆蓋率
|
|
24
|
+
|
|
25
|
+
## 計劃文檔
|
|
26
|
+
|
|
27
|
+
| 文檔 | 描述 | 優先級 |
|
|
28
|
+
|------|------|--------|
|
|
29
|
+
| [01-performance.md](./01-performance.md) | 效能優化計劃 | P0 |
|
|
30
|
+
| [02-architecture.md](./02-architecture.md) | 架構改進計劃 | P1 |
|
|
31
|
+
| [03-api-enhancement.md](./03-api-enhancement.md) | API 增強計劃 | P1 |
|
|
32
|
+
| [04-testing.md](./04-testing.md) | 測試與品質計劃 | P2 |
|
|
33
|
+
|
|
34
|
+
## 實施時程
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
Phase 1 (效能優化) ██████████
|
|
38
|
+
Phase 2 (架構改進) ██████████
|
|
39
|
+
Phase 3 (API 增強) ██████████
|
|
40
|
+
Phase 4 (測試強化) ██████████
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## 相關連結
|
|
44
|
+
|
|
45
|
+
- [CHANGELOG.md](../../CHANGELOG.md)
|
|
46
|
+
- [README.md](../../README.md)
|
|
47
|
+
- [README.zh-TW.md](../../README.zh-TW.md)
|