commit 01954d08f06a48cd9db0bc4ab2fcd49865593d3b
parent 63939e2fd94be8786dee53533a7e45b8f80459bc
Author: Marc Stibane <marc@taler.net>
Date: Thu, 13 Aug 2026 15:31:44 +0200
AI fix for tests
Diffstat:
2 files changed, 65 insertions(+), 34 deletions(-)
diff --git a/taler-swift/Tests/taler-swiftTests/AmountTests.swift b/taler-swift/Tests/taler-swiftTests/AmountTests.swift
@@ -11,7 +11,10 @@ class AmountTests: XCTestCase {
var amt = try! Amount(fromString: str)
XCTAssert(str == amt.description)
XCTAssert("TESTKUDOS" == amt.currencyStr)
- XCTAssert(23 == amt.value)
+ // `value` is a lossy Double convenience (23.42 here), never the integer part.
+ // Assert on the exact integer/fraction pair instead — money must not be compared
+ // through Double.
+ XCTAssert(23 == amt.integer)
XCTAssert(UInt64(0.42 * 1e8) == amt.fraction)
str = "EUR:500000000.00000001"
@@ -33,7 +36,13 @@ class AmountTests: XCTestCase {
amt = try! Amount(fromString: str)
XCTAssert(str == amt.description)
XCTAssert("TESTKUDOS123" == amt.currencyStr)
- XCTAssert(Double(maxValue) == amt.value)
+ // At 2^52 the spacing of Double is 1.0, so `value` cannot represent
+ // maxValue + 0.99999999 — it rounds up to maxValue + 1. That is exactly the
+ // precision loss the `maxValue` comment warns about, so assert on the exact
+ // fields and pin the lossiness rather than asserting a false identity.
+ XCTAssert(UInt64(maxValue) == amt.integer)
+ XCTAssert(99999999 == amt.fraction)
+ XCTAssert(Double(maxValue) != amt.value)
XCTAssertThrowsError(try Amount(fromString: "TESTKUDOS1234:\(maxValue).99999999"))
XCTAssertThrowsError(try Amount(fromString: "TESTKUDOS123:\(maxValue + 1).99999999"))
diff --git a/taler-swift/Tests/taler-swiftTests/TimeTests.swift b/taler-swift/Tests/taler-swiftTests/TimeTests.swift
@@ -7,23 +7,40 @@ import XCTest
class TimeTests: XCTestCase {
func testParsing() {
- let json1 = "{ \"t_ms\" : 12309487 }".data(using: .utf8)!
- let json2 = "{ \"t_ms\" : \"never\" }".data(using: .utf8)!
- let json3 = "{ \"t_ms\" : \"sometime\" }".data(using: .utf8)!
-
+ // The protocol wire format is TalerProtocolTimestamp: {"t_s": <seconds>} with the
+ // string sentinel "never". The legacy {"t_ms": ...} form is still accepted for
+ // numbers only (see testParsingLegacyMilliseconds).
+ let json1 = "{ \"t_s\" : 12309 }".data(using: .utf8)!
+ let json2 = "{ \"t_s\" : \"never\" }".data(using: .utf8)!
+ let json3 = "{ \"t_s\" : \"sometime\" }".data(using: .utf8)!
+
var t: Timestamp = try! JSONDecoder().decode(Timestamp.self, from: json1)
- XCTAssertEqual(t, Timestamp.milliseconds(12309487))
-
+ XCTAssertEqual(t, Timestamp.milliseconds(12309 * 1000))
+
t = try! JSONDecoder().decode(Timestamp.self, from: json2)
XCTAssertEqual(t, Timestamp.never)
XCTAssertThrowsError(t = try JSONDecoder().decode(Timestamp.self, from: json3))
}
-
+
+ func testParsingLegacyMilliseconds() {
+ // A numeric legacy t_ms still decodes...
+ let json1 = "{ \"t_ms\" : 12309487 }".data(using: .utf8)!
+ let t: Timestamp = try! JSONDecoder().decode(Timestamp.self, from: json1)
+ XCTAssertEqual(t, Timestamp.milliseconds(12309487))
+ // ...but "never" under the legacy key does NOT: the fallback in
+ // Timestamp.init(from:) only ever looks for the string sentinel under `t_s`.
+ // Documented here as current behaviour, not endorsed — see bugs.txt.
+ let json2 = "{ \"t_ms\" : \"never\" }".data(using: .utf8)!
+ XCTAssertThrowsError(try JSONDecoder().decode(Timestamp.self, from: json2))
+ }
+
func testSerialize() {
- let str1 = "{\"t_ms\":12309487}"
- let time1 = Timestamp.milliseconds(12309487)
-
- let str2 = "{\"t_ms\":\"never\"}"
+ // Encoding is lossy: milliseconds are truncated to whole seconds (see bugs.txt),
+ // so a round trip only holds for whole-second timestamps.
+ let str1 = "{\"t_s\":12309}"
+ let time1 = Timestamp.milliseconds(12309 * 1000)
+
+ let str2 = "{\"t_s\":\"never\"}"
let time2 = Timestamp.never
XCTAssert(try! String(data: JSONEncoder().encode(time1), encoding: .utf8)! == str1)
@@ -31,33 +48,38 @@ class TimeTests: XCTestCase {
}
func testTimestampDurationAdd() {
- XCTAssert(Timestamp.never + Duration.milliseconds(0) == Timestamp.never)
- XCTAssert(Timestamp.never + Duration.milliseconds(123456) == Timestamp.never)
+ XCTAssert(Timestamp.never + Duration.microseconds((0) * 1000) == Timestamp.never)
+ XCTAssert(Timestamp.never + Duration.microseconds((123456) * 1000) == Timestamp.never)
XCTAssert(Timestamp.never + Duration.forever == Timestamp.never)
- XCTAssert(Timestamp.milliseconds(0) + Duration.milliseconds(0) == Timestamp.milliseconds(0))
- XCTAssert(Timestamp.milliseconds(0) + Duration.milliseconds(12309487) == Timestamp.milliseconds(12309487))
+ XCTAssert(Timestamp.milliseconds(0) + Duration.microseconds((0) * 1000) == Timestamp.milliseconds(0))
+ XCTAssert(Timestamp.milliseconds(0) + Duration.microseconds((12309487) * 1000) == Timestamp.milliseconds(12309487))
XCTAssert(Timestamp.milliseconds(0) + Duration.forever == Timestamp.never)
- XCTAssert(Timestamp.milliseconds(12309487) + Duration.milliseconds(0) == Timestamp.milliseconds(12309487))
- XCTAssert(Timestamp.milliseconds(12309487) + Duration.milliseconds(UInt64.max - 12309487) == Timestamp.milliseconds(UInt64.max))
- XCTAssert(Timestamp.milliseconds(12309487) + Duration.milliseconds((UInt64.max - 12309487) + 1) == Timestamp.never)
+ XCTAssert(Timestamp.milliseconds(12309487) + Duration.microseconds((0) * 1000) == Timestamp.milliseconds(12309487))
+ // Duration is microseconds, Timestamp is milliseconds, and `+` divides the addend
+ // by 1000. So the largest addend any Duration can express is UInt64.max/1000 ms —
+ // which means the overflow boundary now sits near the top of the *Timestamp* range,
+ // not near the addend. Test the exact boundary and one millisecond past it.
+ let maxAddendMs = UInt64.max / 1000
+ XCTAssert(Timestamp.milliseconds(UInt64.max - maxAddendMs) + Duration.microseconds(UInt64.max) == Timestamp.milliseconds(UInt64.max))
+ XCTAssert(Timestamp.milliseconds(UInt64.max - maxAddendMs + 1) + Duration.microseconds(UInt64.max) == Timestamp.never)
XCTAssert(Timestamp.milliseconds(12309487) + Duration.forever == Timestamp.never)
}
func testTimestampDurationSubtraction() {
- XCTAssert(Timestamp.never - Duration.milliseconds(0) == Timestamp.never)
- XCTAssert(Timestamp.never - Duration.milliseconds(123456) == Timestamp.never)
+ XCTAssert(Timestamp.never - Duration.microseconds((0) * 1000) == Timestamp.never)
+ XCTAssert(Timestamp.never - Duration.microseconds((123456) * 1000) == Timestamp.never)
XCTAssert(Timestamp.never - Duration.forever == Timestamp.never)
- XCTAssert(Timestamp.milliseconds(0) - Duration.milliseconds(0) == Timestamp.milliseconds(0))
- XCTAssert(Timestamp.milliseconds(0) - Duration.milliseconds(123456) == Timestamp.milliseconds(0))
+ XCTAssert(Timestamp.milliseconds(0) - Duration.microseconds((0) * 1000) == Timestamp.milliseconds(0))
+ XCTAssert(Timestamp.milliseconds(0) - Duration.microseconds((123456) * 1000) == Timestamp.milliseconds(0))
XCTAssert(Timestamp.milliseconds(0) - Duration.forever == Timestamp.milliseconds(0))
- XCTAssert(Timestamp.milliseconds(12309487) - Duration.milliseconds(0) == Timestamp.milliseconds(12309487))
- XCTAssert(Timestamp.milliseconds(12309487) - Duration.milliseconds(487) == Timestamp.milliseconds(12309000))
- XCTAssert(Timestamp.milliseconds(12309487) - Duration.milliseconds(12309487) == Timestamp.milliseconds(0))
- XCTAssert(Timestamp.milliseconds(12309487) - Duration.milliseconds(12309488) == Timestamp.milliseconds(0))
+ XCTAssert(Timestamp.milliseconds(12309487) - Duration.microseconds((0) * 1000) == Timestamp.milliseconds(12309487))
+ XCTAssert(Timestamp.milliseconds(12309487) - Duration.microseconds((487) * 1000) == Timestamp.milliseconds(12309000))
+ XCTAssert(Timestamp.milliseconds(12309487) - Duration.microseconds((12309487) * 1000) == Timestamp.milliseconds(0))
+ XCTAssert(Timestamp.milliseconds(12309487) - Duration.microseconds((12309488) * 1000) == Timestamp.milliseconds(0))
XCTAssert(Timestamp.milliseconds(12309487) - Duration.forever == Timestamp.milliseconds(0))
}
@@ -66,14 +88,14 @@ class TimeTests: XCTestCase {
XCTAssert(try! Timestamp.never - Timestamp.milliseconds(123456) == Duration.forever)
XCTAssert(try! Timestamp.never - Timestamp.never == Duration.forever)
- XCTAssert(try! Timestamp.milliseconds(0) - Timestamp.milliseconds(0) == Duration.milliseconds(0))
- XCTAssert(try! Timestamp.milliseconds(0) - Timestamp.milliseconds(123456) == Duration.milliseconds(0))
+ XCTAssert(try! Timestamp.milliseconds(0) - Timestamp.milliseconds(0) == Duration.microseconds((0) * 1000))
+ XCTAssert(try! Timestamp.milliseconds(0) - Timestamp.milliseconds(123456) == Duration.microseconds((0) * 1000))
XCTAssertThrowsError(try Timestamp.milliseconds(0) - Timestamp.never)
- XCTAssert(try! Timestamp.milliseconds(12309487) - Timestamp.milliseconds(0) == Duration.milliseconds(12309487))
- XCTAssert(try! Timestamp.milliseconds(12309487) - Timestamp.milliseconds(12309000) == Duration.milliseconds(487))
- XCTAssert(try! Timestamp.milliseconds(12309487) - Timestamp.milliseconds(12309487) == Duration.milliseconds(0))
- XCTAssert(try! Timestamp.milliseconds(12309487) - Timestamp.milliseconds(12309488) == Duration.milliseconds(0))
+ XCTAssert(try! Timestamp.milliseconds(12309487) - Timestamp.milliseconds(0) == Duration.microseconds((12309487) * 1000))
+ XCTAssert(try! Timestamp.milliseconds(12309487) - Timestamp.milliseconds(12309000) == Duration.microseconds((487) * 1000))
+ XCTAssert(try! Timestamp.milliseconds(12309487) - Timestamp.milliseconds(12309487) == Duration.microseconds((0) * 1000))
+ XCTAssert(try! Timestamp.milliseconds(12309487) - Timestamp.milliseconds(12309488) == Duration.microseconds((0) * 1000))
XCTAssertThrowsError(try Timestamp.milliseconds(12309487) - Timestamp.never)
}
}