@aicgen/aicgen 1.0.0-beta.2 → 1.0.1

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 (39) hide show
  1. package/.agent/rules/api-design.md +649 -0
  2. package/.agent/rules/architecture.md +2507 -0
  3. package/.agent/rules/best-practices.md +622 -0
  4. package/.agent/rules/code-style.md +308 -0
  5. package/.agent/rules/design-patterns.md +577 -0
  6. package/.agent/rules/devops.md +230 -0
  7. package/.agent/rules/error-handling.md +417 -0
  8. package/.agent/rules/instructions.md +28 -0
  9. package/.agent/rules/language.md +786 -0
  10. package/.agent/rules/performance.md +710 -0
  11. package/.agent/rules/security.md +587 -0
  12. package/.agent/rules/testing.md +572 -0
  13. package/.agent/workflows/add-documentation.md +10 -0
  14. package/.agent/workflows/generate-integration-tests.md +10 -0
  15. package/.agent/workflows/generate-unit-tests.md +11 -0
  16. package/.agent/workflows/performance-audit.md +11 -0
  17. package/.agent/workflows/refactor-extract-module.md +12 -0
  18. package/.agent/workflows/security-audit.md +12 -0
  19. package/.gemini/instructions.md +4843 -0
  20. package/AGENTS.md +9 -11
  21. package/bun.lock +755 -4
  22. package/claude.md +2 -2
  23. package/config.example.yml +129 -0
  24. package/config.yml +38 -0
  25. package/data/guideline-mappings.yml +128 -0
  26. package/data/language/dart/async.md +289 -0
  27. package/data/language/dart/basics.md +280 -0
  28. package/data/language/dart/error-handling.md +355 -0
  29. package/data/language/dart/index.md +10 -0
  30. package/data/language/dart/testing.md +352 -0
  31. package/data/language/swift/basics.md +477 -0
  32. package/data/language/swift/concurrency.md +654 -0
  33. package/data/language/swift/error-handling.md +679 -0
  34. package/data/language/swift/swiftui-mvvm.md +795 -0
  35. package/data/language/swift/testing.md +708 -0
  36. package/data/version.json +10 -8
  37. package/dist/index.js +50295 -29101
  38. package/jest.config.js +46 -0
  39. package/package.json +13 -2
@@ -0,0 +1,708 @@
1
+ # Swift Testing
2
+
3
+ ## Swift Testing Framework (Swift 6+)
4
+
5
+ The new Swift Testing framework (introduced at WWDC24) provides a modern, expressive API for testing:
6
+
7
+ ```swift
8
+ import Testing
9
+
10
+ // ✅ Simple test with @Test macro
11
+ @Test func additionWorks() {
12
+ #expect(2 + 2 == 4)
13
+ }
14
+
15
+ // ✅ Test with description
16
+ @Test("Addition produces correct results")
17
+ func testAddition() {
18
+ let result = Calculator().add(2, 3)
19
+ #expect(result == 5)
20
+ }
21
+
22
+ // ✅ Async test
23
+ @Test func fetchUserReturnsValidData() async throws {
24
+ let user = try await fetchUser(id: "123")
25
+ #expect(user.id == "123")
26
+ #expect(!user.name.isEmpty)
27
+ }
28
+
29
+ // ✅ Test with throws
30
+ @Test func divisionByZeroThrows() throws {
31
+ #expect(throws: DivisionError.self) {
32
+ try Calculator().divide(10, by: 0)
33
+ }
34
+ }
35
+ ```
36
+
37
+ ## Test Suites
38
+
39
+ Organize tests into logical groups:
40
+
41
+ ```swift
42
+ import Testing
43
+
44
+ // ✅ Test suite with @Suite macro
45
+ @Suite("Calculator Tests")
46
+ struct CalculatorTests {
47
+ let calculator = Calculator()
48
+
49
+ @Test("Addition")
50
+ func testAddition() {
51
+ #expect(calculator.add(2, 3) == 5)
52
+ }
53
+
54
+ @Test("Subtraction")
55
+ func testSubtraction() {
56
+ #expect(calculator.subtract(5, 3) == 2)
57
+ }
58
+ }
59
+
60
+ // ✅ Nested suites for organization
61
+ @Suite("User Management")
62
+ struct UserTests {
63
+ @Suite("Authentication")
64
+ struct AuthTests {
65
+ @Test func loginSucceeds() async throws {
66
+ let result = try await login(email: "test@example.com", password: "password")
67
+ #expect(result.isSuccess)
68
+ }
69
+
70
+ @Test func loginFailsWithInvalidCredentials() async throws {
71
+ await #expect(throws: AuthError.self) {
72
+ try await login(email: "test@example.com", password: "wrong")
73
+ }
74
+ }
75
+ }
76
+
77
+ @Suite("Profile Management")
78
+ struct ProfileTests {
79
+ @Test func updateProfileSucceeds() async throws {
80
+ let user = try await updateProfile(name: "New Name")
81
+ #expect(user.name == "New Name")
82
+ }
83
+ }
84
+ }
85
+ ```
86
+
87
+ ## Parameterized Tests
88
+
89
+ Test multiple scenarios efficiently:
90
+
91
+ ```swift
92
+ import Testing
93
+
94
+ // ✅ Test with multiple arguments
95
+ @Test(arguments: [
96
+ (2, 3, 5),
97
+ (0, 0, 0),
98
+ (-1, 1, 0),
99
+ (100, 200, 300)
100
+ ])
101
+ func additionWorks(a: Int, b: Int, expected: Int) {
102
+ let calculator = Calculator()
103
+ #expect(calculator.add(a, b) == expected)
104
+ }
105
+
106
+ // ✅ Test with array of values
107
+ @Test(arguments: ["alice@example.com", "bob@test.com", "charlie@mail.com"])
108
+ func emailValidationWorks(email: String) {
109
+ #expect(EmailValidator.isValid(email))
110
+ }
111
+
112
+ // ✅ Test combinations
113
+ @Test(arguments: [true, false], [1, 2, 3])
114
+ func featureWorksInAllCombinations(flag: Bool, value: Int) {
115
+ let result = processFeature(enabled: flag, value: value)
116
+ #expect(result != nil)
117
+ }
118
+
119
+ // ✅ Test with custom types
120
+ struct TestCase {
121
+ let input: String
122
+ let expected: Bool
123
+ }
124
+
125
+ @Test(arguments: [
126
+ TestCase(input: "hello", expected: true),
127
+ TestCase(input: "", expected: false),
128
+ TestCase(input: "a", expected: true)
129
+ ])
130
+ func validation(testCase: TestCase) {
131
+ #expect(validate(testCase.input) == testCase.expected)
132
+ }
133
+ ```
134
+
135
+ ## Expectations and Assertions
136
+
137
+ ### Basic Expectations
138
+
139
+ ```swift
140
+ import Testing
141
+
142
+ // ✅ Boolean expectations
143
+ #expect(value == 5)
144
+ #expect(value > 0)
145
+ #expect(name.isEmpty)
146
+ #expect(!isLoading)
147
+
148
+ // ✅ Optional expectations
149
+ #expect(optionalValue != nil)
150
+ #expect(users.first?.name == "Alice")
151
+
152
+ // ✅ Collection expectations
153
+ #expect(array.count == 3)
154
+ #expect(array.contains(item))
155
+ #expect(dictionary["key"] == "value")
156
+ ```
157
+
158
+ ### Error Expectations
159
+
160
+ ```swift
161
+ // ✅ Expect specific error type
162
+ #expect(throws: NetworkError.self) {
163
+ try performNetworkRequest()
164
+ }
165
+
166
+ // ✅ Expect any error
167
+ #expect(throws: Error.self) {
168
+ try riskyOperation()
169
+ }
170
+
171
+ // ✅ Expect no error (default behavior)
172
+ #expect {
173
+ try safeOperation()
174
+ }
175
+
176
+ // ✅ Async error expectations
177
+ await #expect(throws: APIError.unauthorized) {
178
+ try await fetchProtectedResource()
179
+ }
180
+ ```
181
+
182
+ ### Custom Error Messages
183
+
184
+ ```swift
185
+ // ✅ Add context to failures
186
+ #expect(user.age >= 18, "User must be at least 18 years old")
187
+ #expect(items.count > 0, "Shopping cart cannot be empty")
188
+ ```
189
+
190
+ ## Test Lifecycle
191
+
192
+ ### Setup and Teardown
193
+
194
+ ```swift
195
+ import Testing
196
+
197
+ @Suite("Database Tests")
198
+ struct DatabaseTests {
199
+ let database: Database
200
+
201
+ // ✅ Initialize resources before each test
202
+ init() async throws {
203
+ database = try await Database.connect()
204
+ }
205
+
206
+ // ✅ Clean up after tests
207
+ deinit {
208
+ database.disconnect()
209
+ }
210
+
211
+ @Test func insertRecordWorks() async throws {
212
+ try await database.insert(record)
213
+ let fetched = try await database.fetch(id: record.id)
214
+ #expect(fetched == record)
215
+ }
216
+ }
217
+ ```
218
+
219
+ ## XCTest (Traditional Framework)
220
+
221
+ For backwards compatibility and existing codebases:
222
+
223
+ ```swift
224
+ import XCTest
225
+
226
+ // ✅ XCTest class
227
+ class UserServiceTests: XCTestCase {
228
+ var service: UserService!
229
+
230
+ // Setup before each test
231
+ override func setUp() {
232
+ super.setUp()
233
+ service = UserService()
234
+ }
235
+
236
+ // Teardown after each test
237
+ override func tearDown() {
238
+ service = nil
239
+ super.tearDown()
240
+ }
241
+
242
+ // ✅ Test method (must start with "test")
243
+ func testFetchUserSucceeds() async throws {
244
+ let user = try await service.fetchUser(id: "123")
245
+ XCTAssertEqual(user.id, "123")
246
+ XCTAssertFalse(user.name.isEmpty)
247
+ }
248
+
249
+ // ✅ Test expected error
250
+ func testFetchUserThrowsForInvalidID() async {
251
+ await XCTAssertThrowsError(try await service.fetchUser(id: "invalid")) { error in
252
+ XCTAssertTrue(error is ServiceError)
253
+ }
254
+ }
255
+
256
+ // ✅ Async test
257
+ func testAsyncOperation() async throws {
258
+ let result = try await service.performAsyncOperation()
259
+ XCTAssertTrue(result.success)
260
+ }
261
+ }
262
+ ```
263
+
264
+ ## Common XCTest Assertions
265
+
266
+ ```swift
267
+ // Equality
268
+ XCTAssertEqual(actual, expected)
269
+ XCTAssertNotEqual(actual, unexpected)
270
+
271
+ // Boolean
272
+ XCTAssertTrue(condition)
273
+ XCTAssertFalse(condition)
274
+
275
+ // Nil checks
276
+ XCTAssertNil(optionalValue)
277
+ XCTAssertNotNil(optionalValue)
278
+
279
+ // Errors
280
+ XCTAssertThrowsError(try dangerousOperation())
281
+ XCTAssertNoThrow(try safeOperation())
282
+
283
+ // Numeric comparisons
284
+ XCTAssertGreaterThan(value, 0)
285
+ XCTAssertLessThan(value, 100)
286
+
287
+ // Custom messages
288
+ XCTAssertEqual(user.age, 18, "User age should be 18")
289
+ ```
290
+
291
+ ## Testing Async Code
292
+
293
+ ### Swift Testing Framework
294
+
295
+ ```swift
296
+ import Testing
297
+
298
+ // ✅ Simple async test
299
+ @Test func fetchDataCompletes() async throws {
300
+ let data = try await fetchData()
301
+ #expect(!data.isEmpty)
302
+ }
303
+
304
+ // ✅ Test with timeout (future feature)
305
+ @Test(.timeLimit(.seconds(5)))
306
+ func slowOperationCompletes() async throws {
307
+ try await performSlowOperation()
308
+ }
309
+
310
+ // ✅ Test concurrent operations
311
+ @Test func concurrentFetchesSucceed() async throws {
312
+ async let user1 = fetchUser(id: "1")
313
+ async let user2 = fetchUser(id: "2")
314
+ async let user3 = fetchUser(id: "3")
315
+
316
+ let users = try await [user1, user2, user3]
317
+ #expect(users.count == 3)
318
+ }
319
+ ```
320
+
321
+ ### XCTest with Async
322
+
323
+ ```swift
324
+ import XCTest
325
+
326
+ class AsyncTests: XCTestCase {
327
+ // ✅ Async test method
328
+ func testAsyncOperation() async throws {
329
+ let result = try await fetchData()
330
+ XCTAssertFalse(result.isEmpty)
331
+ }
332
+
333
+ // ✅ Test with expectation (older pattern)
334
+ func testCallbackCompletion() {
335
+ let expectation = expectation(description: "Callback called")
336
+
337
+ fetchDataWithCallback { result in
338
+ XCTAssertNotNil(result)
339
+ expectation.fulfill()
340
+ }
341
+
342
+ waitForExpectations(timeout: 5)
343
+ }
344
+ }
345
+ ```
346
+
347
+ ## Mocking and Test Doubles
348
+
349
+ ### Protocol-Based Mocking
350
+
351
+ ```swift
352
+ // ✅ Define protocol for dependency
353
+ protocol UserRepository {
354
+ func fetchUser(id: String) async throws -> User
355
+ func saveUser(_ user: User) async throws
356
+ }
357
+
358
+ // Real implementation
359
+ class ProductionUserRepository: UserRepository {
360
+ func fetchUser(id: String) async throws -> User {
361
+ // Real API call
362
+ }
363
+
364
+ func saveUser(_ user: User) async throws {
365
+ // Real save
366
+ }
367
+ }
368
+
369
+ // ✅ Mock for testing
370
+ class MockUserRepository: UserRepository {
371
+ var fetchUserCalled = false
372
+ var userToReturn: User?
373
+ var errorToThrow: Error?
374
+
375
+ func fetchUser(id: String) async throws -> User {
376
+ fetchUserCalled = true
377
+
378
+ if let error = errorToThrow {
379
+ throw error
380
+ }
381
+
382
+ guard let user = userToReturn else {
383
+ throw MockError.noUserSet
384
+ }
385
+
386
+ return user
387
+ }
388
+
389
+ func saveUser(_ user: User) async throws {
390
+ // Store for verification
391
+ }
392
+ }
393
+
394
+ // Test usage
395
+ @Test func serviceUsesMockRepository() async throws {
396
+ let mockRepo = MockUserRepository()
397
+ mockRepo.userToReturn = User(id: "123", name: "Alice")
398
+
399
+ let service = UserService(repository: mockRepo)
400
+ let user = try await service.getUser(id: "123")
401
+
402
+ #expect(mockRepo.fetchUserCalled)
403
+ #expect(user.name == "Alice")
404
+ }
405
+ ```
406
+
407
+ ### Spy Pattern
408
+
409
+ ```swift
410
+ // ✅ Spy records method calls
411
+ class SpyUserRepository: UserRepository {
412
+ var fetchUserCallCount = 0
413
+ var fetchedUserIDs: [String] = []
414
+ var savedUsers: [User] = []
415
+
416
+ func fetchUser(id: String) async throws -> User {
417
+ fetchUserCallCount += 1
418
+ fetchedUserIDs.append(id)
419
+ return User(id: id, name: "Test User")
420
+ }
421
+
422
+ func saveUser(_ user: User) async throws {
423
+ savedUsers.append(user)
424
+ }
425
+ }
426
+
427
+ @Test func serviceCallsRepositoryCorrectly() async throws {
428
+ let spy = SpyUserRepository()
429
+ let service = UserService(repository: spy)
430
+
431
+ try await service.getUser(id: "123")
432
+ try await service.getUser(id: "456")
433
+
434
+ #expect(spy.fetchUserCallCount == 2)
435
+ #expect(spy.fetchedUserIDs == ["123", "456"])
436
+ }
437
+ ```
438
+
439
+ ## Testing Best Practices
440
+
441
+ ### Write Focused Tests
442
+
443
+ ```swift
444
+ // ❌ Testing too much in one test
445
+ @Test func userManagement() async throws {
446
+ let user = try await createUser(name: "Alice")
447
+ let updated = try await updateUser(user, name: "Bob")
448
+ try await deleteUser(updated.id)
449
+ let fetched = try? await fetchUser(id: updated.id)
450
+ #expect(fetched == nil)
451
+ }
452
+
453
+ // ✅ One test per behavior
454
+ @Test func createUserSucceeds() async throws {
455
+ let user = try await createUser(name: "Alice")
456
+ #expect(user.name == "Alice")
457
+ }
458
+
459
+ @Test func updateUserChangesName() async throws {
460
+ let user = try await createUser(name: "Alice")
461
+ let updated = try await updateUser(user, name: "Bob")
462
+ #expect(updated.name == "Bob")
463
+ }
464
+
465
+ @Test func deleteUserRemovesFromDatabase() async throws {
466
+ let user = try await createUser(name: "Alice")
467
+ try await deleteUser(user.id)
468
+ await #expect(throws: NotFoundError.self) {
469
+ try await fetchUser(id: user.id)
470
+ }
471
+ }
472
+ ```
473
+
474
+ ### Test Edge Cases
475
+
476
+ ```swift
477
+ @Suite("String Validation Tests")
478
+ struct ValidationTests {
479
+ @Test func validEmailPasses() {
480
+ #expect(validate("test@example.com"))
481
+ }
482
+
483
+ @Test func emptyStringFails() {
484
+ #expect(!validate(""))
485
+ }
486
+
487
+ @Test func stringWithoutAtSymbolFails() {
488
+ #expect(!validate("notanemail"))
489
+ }
490
+
491
+ @Test func veryLongEmailFails() {
492
+ let longEmail = String(repeating: "a", count: 300) + "@example.com"
493
+ #expect(!validate(longEmail))
494
+ }
495
+
496
+ @Test func emailWithSpecialCharactersWorks() {
497
+ #expect(validate("user+tag@example.co.uk"))
498
+ }
499
+ }
500
+ ```
501
+
502
+ ### Use Descriptive Names
503
+
504
+ ```swift
505
+ // ❌ Unclear test names
506
+ @Test func test1() { }
507
+ @Test func testUser() { }
508
+
509
+ // ✅ Descriptive names that explain what is tested
510
+ @Test("User registration succeeds with valid email")
511
+ func userRegistrationWithValidEmail() { }
512
+
513
+ @Test("Login fails when password is incorrect")
514
+ func loginWithIncorrectPassword() { }
515
+
516
+ @Test("Shopping cart total includes tax")
517
+ func cartTotalCalculation() { }
518
+ ```
519
+
520
+ ### Avoid Test Interdependence
521
+
522
+ ```swift
523
+ // ❌ Tests depend on execution order
524
+ class BadTests: XCTestCase {
525
+ static var userId: String?
526
+
527
+ func test1_createUser() {
528
+ BadTests.userId = createUser().id
529
+ }
530
+
531
+ func test2_updateUser() {
532
+ updateUser(id: BadTests.userId!) // Fails if test1 doesn't run first
533
+ }
534
+ }
535
+
536
+ // ✅ Each test is independent
537
+ @Suite("User Tests")
538
+ struct GoodTests {
539
+ @Test func userCreation() async throws {
540
+ let user = try await createUser(name: "Alice")
541
+ #expect(user.name == "Alice")
542
+ }
543
+
544
+ @Test func userUpdate() async throws {
545
+ // Create user within this test
546
+ let user = try await createUser(name: "Alice")
547
+ let updated = try await updateUser(user, name: "Bob")
548
+ #expect(updated.name == "Bob")
549
+ }
550
+ }
551
+ ```
552
+
553
+ ## Testing View Models
554
+
555
+ ```swift
556
+ import Testing
557
+
558
+ @Suite("User View Model Tests")
559
+ @MainActor
560
+ struct UserViewModelTests {
561
+ @Test func loadingUsersUpdatesState() async {
562
+ let mockRepo = MockUserRepository()
563
+ mockRepo.usersToReturn = [
564
+ User(id: "1", name: "Alice"),
565
+ User(id: "2", name: "Bob")
566
+ ]
567
+
568
+ let viewModel = UserViewModel(repository: mockRepo)
569
+ await viewModel.loadUsers()
570
+
571
+ #expect(viewModel.users.count == 2)
572
+ #expect(!viewModel.isLoading)
573
+ #expect(viewModel.errorMessage == nil)
574
+ }
575
+
576
+ @Test func loadingUsersHandlesErrors() async {
577
+ let mockRepo = MockUserRepository()
578
+ mockRepo.errorToThrow = NetworkError.connectionFailed
579
+
580
+ let viewModel = UserViewModel(repository: mockRepo)
581
+ await viewModel.loadUsers()
582
+
583
+ #expect(viewModel.users.isEmpty)
584
+ #expect(!viewModel.isLoading)
585
+ #expect(viewModel.errorMessage != nil)
586
+ }
587
+
588
+ @Test func deletingUserRemovesFromList() async {
589
+ let mockRepo = MockUserRepository()
590
+ let user1 = User(id: "1", name: "Alice")
591
+ let user2 = User(id: "2", name: "Bob")
592
+ mockRepo.usersToReturn = [user1, user2]
593
+
594
+ let viewModel = UserViewModel(repository: mockRepo)
595
+ await viewModel.loadUsers()
596
+ await viewModel.deleteUser(user1)
597
+
598
+ #expect(viewModel.users.count == 1)
599
+ #expect(viewModel.users.first?.id == "2")
600
+ }
601
+ }
602
+ ```
603
+
604
+ ## Performance Testing
605
+
606
+ ### Swift Testing
607
+
608
+ ```swift
609
+ import Testing
610
+
611
+ // ✅ Measure performance
612
+ @Test func sortingLargeArray() {
613
+ let array = (0..<10000).shuffled()
614
+ measure {
615
+ _ = array.sorted()
616
+ }
617
+ }
618
+ ```
619
+
620
+ ### XCTest Performance
621
+
622
+ ```swift
623
+ import XCTest
624
+
625
+ class PerformanceTests: XCTestCase {
626
+ func testSortingPerformance() {
627
+ let array = (0..<10000).shuffled()
628
+
629
+ measure {
630
+ _ = array.sorted()
631
+ }
632
+ }
633
+
634
+ func testDatabaseQueryPerformance() async {
635
+ await measureAsync {
636
+ try? await database.fetchAllRecords()
637
+ }
638
+ }
639
+ }
640
+
641
+ extension XCTestCase {
642
+ func measureAsync(_ block: @escaping () async -> Void) async {
643
+ measure {
644
+ let expectation = expectation(description: "Async operation")
645
+ Task {
646
+ await block()
647
+ expectation.fulfill()
648
+ }
649
+ wait(for: [expectation], timeout: 10)
650
+ }
651
+ }
652
+ }
653
+ ```
654
+
655
+ ## Test Organization
656
+
657
+ ### File Structure
658
+
659
+ ```
660
+ Tests/
661
+ ├── UnitTests/
662
+ │ ├── Models/
663
+ │ │ ├── UserTests.swift
664
+ │ │ └── ProductTests.swift
665
+ │ ├── Services/
666
+ │ │ ├── UserServiceTests.swift
667
+ │ │ └── AuthServiceTests.swift
668
+ │ └── ViewModels/
669
+ │ ├── UserViewModelTests.swift
670
+ │ └── ProductViewModelTests.swift
671
+ ├── IntegrationTests/
672
+ │ ├── APITests.swift
673
+ │ └── DatabaseTests.swift
674
+ └── TestHelpers/
675
+ ├── Mocks.swift
676
+ └── Fixtures.swift
677
+ ```
678
+
679
+ ### Shared Test Utilities
680
+
681
+ ```swift
682
+ // ✅ Create reusable test helpers
683
+ enum TestFixtures {
684
+ static func makeUser(id: String = "123", name: String = "Test User") -> User {
685
+ User(id: id, name: name, email: "\(name)@test.com")
686
+ }
687
+
688
+ static func makeUsers(count: Int) -> [User] {
689
+ (0..<count).map { i in
690
+ makeUser(id: "\(i)", name: "User \(i)")
691
+ }
692
+ }
693
+ }
694
+
695
+ // Usage in tests
696
+ @Test func processMultipleUsers() {
697
+ let users = TestFixtures.makeUsers(count: 5)
698
+ #expect(users.count == 5)
699
+ }
700
+ ```
701
+
702
+ ---
703
+
704
+ **Sources:**
705
+ - [Swift Testing (WWDC24)](https://developer.apple.com/videos/play/wwdc2024/10179/)
706
+ - [Apple XCTest Documentation](https://developer.apple.com/documentation/xctest)
707
+ - [Testing Swift](https://www.swiftbysundell.com/basics/testing/)
708
+ - [Swift.org: Testing](https://www.swift.org/documentation/testing/)