Skip to content

Commit 20c2d2c

Browse files
committed
test: increase test coverage across project
1 parent de8419e commit 20c2d2c

File tree

10 files changed

+1882
-0
lines changed

10 files changed

+1882
-0
lines changed

.serena/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/cache

.serena/project.yml

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# language of the project (csharp, python, rust, java, typescript, go, cpp, or ruby)
2+
# * For C, use cpp
3+
# * For JavaScript, use typescript
4+
# Special requirements:
5+
# * csharp: Requires the presence of a .sln file in the project folder.
6+
language: swift
7+
8+
# whether to use the project's gitignore file to ignore files
9+
# Added on 2025-04-07
10+
ignore_all_files_in_gitignore: true
11+
# list of additional paths to ignore
12+
# same syntax as gitignore, so you can use * and **
13+
# Was previously called `ignored_dirs`, please update your config if you are using that.
14+
# Added (renamed) on 2025-04-07
15+
ignored_paths: []
16+
17+
# whether the project is in read-only mode
18+
# If set to true, all editing tools will be disabled and attempts to use them will result in an error
19+
# Added on 2025-04-18
20+
read_only: false
21+
22+
# list of tool names to exclude. We recommend not excluding any tools, see the readme for more details.
23+
# Below is the complete list of tools for convenience.
24+
# To make sure you have the latest list of tools, and to view their descriptions,
25+
# execute `uv run scripts/print_tool_overview.py`.
26+
#
27+
# * `activate_project`: Activates a project by name.
28+
# * `check_onboarding_performed`: Checks whether project onboarding was already performed.
29+
# * `create_text_file`: Creates/overwrites a file in the project directory.
30+
# * `delete_lines`: Deletes a range of lines within a file.
31+
# * `delete_memory`: Deletes a memory from Serena's project-specific memory store.
32+
# * `execute_shell_command`: Executes a shell command.
33+
# * `find_referencing_code_snippets`: Finds code snippets in which the symbol at the given location is referenced.
34+
# * `find_referencing_symbols`: Finds symbols that reference the symbol at the given location (optionally filtered by type).
35+
# * `find_symbol`: Performs a global (or local) search for symbols with/containing a given name/substring (optionally filtered by type).
36+
# * `get_current_config`: Prints the current configuration of the agent, including the active and available projects, tools, contexts, and modes.
37+
# * `get_symbols_overview`: Gets an overview of the top-level symbols defined in a given file.
38+
# * `initial_instructions`: Gets the initial instructions for the current project.
39+
# Should only be used in settings where the system prompt cannot be set,
40+
# e.g. in clients you have no control over, like Claude Desktop.
41+
# * `insert_after_symbol`: Inserts content after the end of the definition of a given symbol.
42+
# * `insert_at_line`: Inserts content at a given line in a file.
43+
# * `insert_before_symbol`: Inserts content before the beginning of the definition of a given symbol.
44+
# * `list_dir`: Lists files and directories in the given directory (optionally with recursion).
45+
# * `list_memories`: Lists memories in Serena's project-specific memory store.
46+
# * `onboarding`: Performs onboarding (identifying the project structure and essential tasks, e.g. for testing or building).
47+
# * `prepare_for_new_conversation`: Provides instructions for preparing for a new conversation (in order to continue with the necessary context).
48+
# * `read_file`: Reads a file within the project directory.
49+
# * `read_memory`: Reads the memory with the given name from Serena's project-specific memory store.
50+
# * `remove_project`: Removes a project from the Serena configuration.
51+
# * `replace_lines`: Replaces a range of lines within a file with new content.
52+
# * `replace_symbol_body`: Replaces the full definition of a symbol.
53+
# * `restart_language_server`: Restarts the language server, may be necessary when edits not through Serena happen.
54+
# * `search_for_pattern`: Performs a search for a pattern in the project.
55+
# * `summarize_changes`: Provides instructions for summarizing the changes made to the codebase.
56+
# * `switch_modes`: Activates modes by providing a list of their names
57+
# * `think_about_collected_information`: Thinking tool for pondering the completeness of collected information.
58+
# * `think_about_task_adherence`: Thinking tool for determining whether the agent is still on track with the current task.
59+
# * `think_about_whether_you_are_done`: Thinking tool for determining whether the task is truly completed.
60+
# * `write_memory`: Writes a named memory (for future reference) to Serena's project-specific memory store.
61+
excluded_tools: []
62+
63+
# initial prompt for the project. It will always be given to the LLM upon activating the project
64+
# (contrary to the memories, which are loaded on demand).
65+
initial_prompt: ""
66+
67+
project_name: "supabase-swift"

Tests/AuthTests/AuthErrorTests.swift

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,100 @@ final class AuthErrorTests: XCTestCase {
4242
XCTAssertEqual(implicitGrantRedirect.errorCode, .unknown)
4343
XCTAssertEqual(implicitGrantRedirect.message, "Implicit grant failure")
4444
}
45+
46+
func testWeakPasswordWithReasons() {
47+
let reasons = ["length", "characters", "pwned"]
48+
let weakPassword = AuthError.weakPassword(message: "Password is weak", reasons: reasons)
49+
50+
XCTAssertEqual(weakPassword.message, "Password is weak")
51+
XCTAssertEqual(weakPassword.errorCode, .weakPassword)
52+
XCTAssertEqual(weakPassword.errorDescription, "Password is weak")
53+
}
54+
55+
func testJWTVerificationFailed() {
56+
let jwtError = AuthError.jwtVerificationFailed(message: "Invalid JWT signature")
57+
58+
XCTAssertEqual(jwtError.message, "Invalid JWT signature")
59+
XCTAssertEqual(jwtError.errorCode, .invalidJWT)
60+
XCTAssertEqual(jwtError.errorDescription, "Invalid JWT signature")
61+
}
62+
63+
func testPKCEGrantCodeExchangeWithErrorAndCode() {
64+
let pkceError = AuthError.pkceGrantCodeExchange(
65+
message: "Exchange failed",
66+
error: "invalid_grant",
67+
code: "auth_code_123"
68+
)
69+
70+
XCTAssertEqual(pkceError.message, "Exchange failed")
71+
XCTAssertEqual(pkceError.errorCode, .unknown)
72+
}
73+
74+
func testAPIErrorWithDifferentCodes() {
75+
let errorCodes: [ErrorCode] = [
76+
.badJWT,
77+
.sessionExpired,
78+
.userNotFound,
79+
.invalidCredentials,
80+
.emailExists,
81+
.overRequestRateLimit
82+
]
83+
84+
for code in errorCodes {
85+
let error = AuthError.api(
86+
message: "Test error",
87+
errorCode: code,
88+
underlyingData: Data(),
89+
underlyingResponse: HTTPURLResponse(
90+
url: URL(string: "http://localhost")!,
91+
statusCode: 400,
92+
httpVersion: nil,
93+
headerFields: nil
94+
)!
95+
)
96+
97+
XCTAssertEqual(error.errorCode, code)
98+
XCTAssertEqual(error.message, "Test error")
99+
}
100+
}
101+
102+
func testErrorCodeEquality() {
103+
XCTAssertEqual(ErrorCode.badJWT, ErrorCode("bad_jwt"))
104+
XCTAssertEqual(ErrorCode.sessionExpired, ErrorCode("session_expired"))
105+
XCTAssertNotEqual(ErrorCode.badJWT, ErrorCode.sessionExpired)
106+
}
107+
108+
func testErrorCodeRawValue() {
109+
XCTAssertEqual(ErrorCode.badJWT.rawValue, "bad_jwt")
110+
XCTAssertEqual(ErrorCode.sessionExpired.rawValue, "session_expired")
111+
XCTAssertEqual(ErrorCode.unknown.rawValue, "unknown")
112+
}
113+
114+
func testErrorCodeInitWithString() {
115+
let code1 = ErrorCode("custom_error")
116+
XCTAssertEqual(code1.rawValue, "custom_error")
117+
118+
let code2 = ErrorCode(rawValue: "another_error")
119+
XCTAssertEqual(code2.rawValue, "another_error")
120+
}
121+
122+
func testErrorCodeHashable() {
123+
let set: Set<ErrorCode> = [.badJWT, .sessionExpired, .userNotFound]
124+
XCTAssertTrue(set.contains(.badJWT))
125+
XCTAssertTrue(set.contains(.sessionExpired))
126+
XCTAssertFalse(set.contains(.emailExists))
127+
}
128+
129+
func testAuthErrorPatternMatching() {
130+
let error1: Error = AuthError.sessionMissing
131+
XCTAssertTrue(AuthError.sessionMissing ~= error1)
132+
133+
let error2: Error = AuthError.weakPassword(message: "weak", reasons: [])
134+
XCTAssertTrue(AuthError.weakPassword(message: "weak", reasons: []) ~= error2)
135+
136+
// Test non-AuthError
137+
struct OtherError: Error {}
138+
let error3: Error = OtherError()
139+
XCTAssertFalse(AuthError.sessionMissing ~= error3)
140+
}
45141
}
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
//
2+
// JWTCryptoTests.swift
3+
// Supabase
4+
//
5+
// Created by Coverage Tests
6+
//
7+
8+
import XCTest
9+
@testable import Auth
10+
@testable import Helpers
11+
12+
#if canImport(Security)
13+
final class JWTCryptoTests: XCTestCase {
14+
15+
// MARK: - JWK+RSA Tests
16+
17+
func testRSAPublishKeyGeneration() {
18+
// Test data from a real RS256 JWT (modulus and exponent)
19+
// This is a sample RSA256 public key
20+
let jwk = JWK(
21+
kty: "RSA",
22+
keyOps: ["verify"],
23+
alg: "RS256",
24+
kid: "test-key-1",
25+
n: "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw",
26+
e: "AQAB",
27+
crv: nil,
28+
x: nil,
29+
y: nil,
30+
k: nil
31+
)
32+
33+
// Test valid RSA key generation
34+
let rsaKey = jwk.rsaPublishKey
35+
XCTAssertNotNil(rsaKey, "RSA public key should be generated successfully")
36+
}
37+
38+
func testRSAPublishKeyInvalidAlgorithm() {
39+
// Test with invalid algorithm
40+
let jwk = JWK(
41+
kty: "RSA",
42+
keyOps: nil,
43+
alg: "ES256", // Wrong algorithm - should be RS256
44+
kid: "test-key-2",
45+
n: "test-modulus",
46+
e: "AQAB",
47+
crv: nil,
48+
x: nil,
49+
y: nil,
50+
k: nil
51+
)
52+
53+
let rsaKey = jwk.rsaPublishKey
54+
XCTAssertNil(rsaKey, "RSA public key should be nil with wrong algorithm")
55+
}
56+
57+
func testRSAPublishKeyInvalidKeyType() {
58+
// Test with invalid key type
59+
let jwk = JWK(
60+
kty: "EC", // Wrong type - should be RSA
61+
keyOps: nil,
62+
alg: "RS256",
63+
kid: "test-key-3",
64+
n: "test-modulus",
65+
e: "AQAB",
66+
crv: nil,
67+
x: nil,
68+
y: nil,
69+
k: nil
70+
)
71+
72+
let rsaKey = jwk.rsaPublishKey
73+
XCTAssertNil(rsaKey, "RSA public key should be nil with wrong key type")
74+
}
75+
76+
func testRSAPublishKeyMissingModulus() {
77+
// Test with missing modulus
78+
let jwk = JWK(
79+
kty: "RSA",
80+
keyOps: nil,
81+
alg: "RS256",
82+
kid: "test-key-4",
83+
n: nil, // Missing modulus
84+
e: "AQAB",
85+
crv: nil,
86+
x: nil,
87+
y: nil,
88+
k: nil
89+
)
90+
91+
let rsaKey = jwk.rsaPublishKey
92+
XCTAssertNil(rsaKey, "RSA public key should be nil with missing modulus")
93+
}
94+
95+
func testRSAPublishKeyMissingExponent() {
96+
// Test with missing exponent
97+
let jwk = JWK(
98+
kty: "RSA",
99+
keyOps: nil,
100+
alg: "RS256",
101+
kid: "test-key-5",
102+
n: "test-modulus",
103+
e: nil, // Missing exponent
104+
crv: nil,
105+
x: nil,
106+
y: nil,
107+
k: nil
108+
)
109+
110+
let rsaKey = jwk.rsaPublishKey
111+
XCTAssertNil(rsaKey, "RSA public key should be nil with missing exponent")
112+
}
113+
114+
func testRSAPublishKeyInvalidBase64() {
115+
// Test with invalid Base64URL data
116+
let jwk = JWK(
117+
kty: "RSA",
118+
keyOps: nil,
119+
alg: "RS256",
120+
kid: "test-key-6",
121+
n: "!!!invalid-base64!!!",
122+
e: "AQAB",
123+
crv: nil,
124+
x: nil,
125+
y: nil,
126+
k: nil
127+
)
128+
129+
let rsaKey = jwk.rsaPublishKey
130+
XCTAssertNil(rsaKey, "RSA public key should be nil with invalid base64 modulus")
131+
}
132+
133+
// MARK: - JWTAlgorithm Tests
134+
135+
func testRS256VerificationWithValidSignature() {
136+
// Create a sample JWT token (this would normally come from a real auth server)
137+
// For testing, we'll use a known-good JWT
138+
let header = #"{"alg":"RS256","typ":"JWT"}"#
139+
let payload = #"{"sub":"1234567890","name":"Test User","iat":1516239022}"#
140+
141+
guard
142+
let headerData = header.data(using: .utf8),
143+
let payloadData = payload.data(using: .utf8)
144+
else {
145+
XCTFail("Failed to create test data")
146+
return
147+
}
148+
149+
let headerB64 = Base64URL.encode(headerData)
150+
let payloadB64 = Base64URL.encode(payloadData)
151+
152+
// Create a mock signature (in real scenario, this would be a proper RSA signature)
153+
let mockSignature = Data([0x00, 0x01, 0x02, 0x03])
154+
let signatureB64 = Base64URL.encode(mockSignature)
155+
156+
let jwtString = "\(headerB64).\(payloadB64).\(signatureB64)"
157+
158+
// Decode the JWT
159+
guard let decoded = JWT.decode(jwtString) else {
160+
XCTFail("Failed to decode JWT")
161+
return
162+
}
163+
164+
XCTAssertEqual(decoded.raw.header, headerB64)
165+
XCTAssertEqual(decoded.raw.payload, payloadB64)
166+
XCTAssertEqual(decoded.signature, mockSignature)
167+
}
168+
169+
func testRS256AlgorithmType() {
170+
let algorithm = JWTAlgorithm.rs256
171+
XCTAssertEqual(algorithm.rawValue, "RS256")
172+
}
173+
174+
}
175+
#endif

0 commit comments

Comments
 (0)