v1.0.0 — initiale Extraktion aus memoro-native
ManaCore + ManaTokens als Swift-Package für alle nativen mana-e.V.-Apps. Phase α aus mana/docs/MANA_SWIFT.md durch. ManaCore: - AuthClient gegen mana-auth (Login, Refresh, Status-Maschine) - AuthenticatedTransport (URLSession + 401-Retry) - ManaAppConfig-Protocol für App-injizierbare Konfig - KeychainStore mit optionaler Shared-Access-Group - JWT-Parser für lokale Expiry-Prüfung - AuthError, CoreLog (interne OSLog-Logger) ManaTokens: - 12 Vereins-Tokens als dynamic Light/Dark Colors - 5 Brand-Literale (mana-yellow, spectrum-orange, ...) - Spacing, Radius, Typography aus mana/docs/THEMING.md Tests: 12 Unit-Tests grün via swift test. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
commit
df6f67ee45
23 changed files with 1151 additions and 0 deletions
37
Tests/ManaCoreTests/JWTTests.swift
Normal file
37
Tests/ManaCoreTests/JWTTests.swift
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
import Foundation
|
||||
import Testing
|
||||
@testable import ManaCore
|
||||
|
||||
@Suite("JWT")
|
||||
struct JWTTests {
|
||||
@Test("Liefert expiry aus gültigem JWT")
|
||||
func extractsExpiry() throws {
|
||||
// Header: {"alg":"HS256","typ":"JWT"}
|
||||
// Payload: {"sub":"u1","exp":2000000000}
|
||||
let token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1MSIsImV4cCI6MjAwMDAwMDAwMH0.signature"
|
||||
let expiry = try #require(JWT.expiry(of: token))
|
||||
#expect(expiry == Date(timeIntervalSince1970: 2_000_000_000))
|
||||
}
|
||||
|
||||
@Test("Nil für nicht-JWT-Strings")
|
||||
func nilForNonJWT() {
|
||||
#expect(JWT.expiry(of: "nonsense") == nil)
|
||||
#expect(JWT.expiry(of: "a.b") == nil)
|
||||
#expect(JWT.expiry(of: "a.b.c.d") == nil)
|
||||
}
|
||||
|
||||
@Test("Nil wenn exp-Claim fehlt")
|
||||
func nilWhenExpMissing() {
|
||||
// Payload: {"sub":"u1"}
|
||||
let token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1MSJ9.signature"
|
||||
#expect(JWT.expiry(of: token) == nil)
|
||||
}
|
||||
|
||||
@Test("Toleriert Base64URL-Padding")
|
||||
func toleratesBase64URLPadding() throws {
|
||||
// Payload ohne Padding: {"exp":1700000000}
|
||||
let token = "eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE3MDAwMDAwMDB9.sig"
|
||||
let expiry = try #require(JWT.expiry(of: token))
|
||||
#expect(expiry == Date(timeIntervalSince1970: 1_700_000_000))
|
||||
}
|
||||
}
|
||||
27
Tests/ManaCoreTests/ManaAppConfigTests.swift
Normal file
27
Tests/ManaCoreTests/ManaAppConfigTests.swift
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
import Foundation
|
||||
import Testing
|
||||
@testable import ManaCore
|
||||
|
||||
@Suite("ManaAppConfig")
|
||||
struct ManaAppConfigTests {
|
||||
@Test("DefaultManaAppConfig setzt Felder")
|
||||
func defaultSetsFields() throws {
|
||||
let config = DefaultManaAppConfig(
|
||||
authBaseURL: URL(string: "https://auth.mana.how")!,
|
||||
keychainService: "ev.mana.memoro",
|
||||
keychainAccessGroup: "TEAMID.ev.mana.shared"
|
||||
)
|
||||
#expect(config.authBaseURL.absoluteString == "https://auth.mana.how")
|
||||
#expect(config.keychainService == "ev.mana.memoro")
|
||||
#expect(config.keychainAccessGroup == "TEAMID.ev.mana.shared")
|
||||
}
|
||||
|
||||
@Test("AccessGroup ist optional")
|
||||
func accessGroupOptional() {
|
||||
let config = DefaultManaAppConfig(
|
||||
authBaseURL: URL(string: "https://auth.mana.how")!,
|
||||
keychainService: "ev.mana.cards"
|
||||
)
|
||||
#expect(config.keychainAccessGroup == nil)
|
||||
}
|
||||
}
|
||||
75
Tests/ManaTokensTests/ColorTests.swift
Normal file
75
Tests/ManaTokensTests/ColorTests.swift
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
import SwiftUI
|
||||
import Testing
|
||||
@testable import ManaTokens
|
||||
|
||||
@Suite("ManaTokens Colors")
|
||||
struct ColorTests {
|
||||
@Test("HSL Pure Schwarz")
|
||||
func hslPureBlack() {
|
||||
let color = PlatformColor.fromHSL(0, 0, 0)
|
||||
let components = rgbComponents(of: color)
|
||||
#expect(components.r == 0)
|
||||
#expect(components.g == 0)
|
||||
#expect(components.b == 0)
|
||||
}
|
||||
|
||||
@Test("HSL Pure Weiß")
|
||||
func hslPureWhite() {
|
||||
let color = PlatformColor.fromHSL(0, 0, 100)
|
||||
let components = rgbComponents(of: color)
|
||||
#expect(components.r == 1)
|
||||
#expect(components.g == 1)
|
||||
#expect(components.b == 1)
|
||||
}
|
||||
|
||||
@Test("HSL Rot 0 100 50")
|
||||
func hslPureRed() {
|
||||
let color = PlatformColor.fromHSL(0, 100, 50)
|
||||
let components = rgbComponents(of: color)
|
||||
#expect(approxEqual(components.r, 1))
|
||||
#expect(approxEqual(components.g, 0))
|
||||
#expect(approxEqual(components.b, 0))
|
||||
}
|
||||
|
||||
@Test("HSL Spektrum-Orange 25 100 50")
|
||||
func hslSpectrumOrange() {
|
||||
// Mana-Variant primary aus THEMING.md
|
||||
let color = PlatformColor.fromHSL(25, 100, 50)
|
||||
let components = rgbComponents(of: color)
|
||||
#expect(approxEqual(components.r, 1.0))
|
||||
#expect(approxEqual(components.g, 0.4167, tolerance: 0.01))
|
||||
#expect(approxEqual(components.b, 0))
|
||||
}
|
||||
|
||||
@Test("ManaColor.primary ist abrufbar")
|
||||
func manaColorPrimary() {
|
||||
let _: Color = ManaColor.primary
|
||||
let _: Color = ManaColor.background
|
||||
let _: Color = ManaColor.foreground
|
||||
}
|
||||
|
||||
@Test("ManaBrand.manaYellow entspricht FFB700")
|
||||
func manaBrandYellow() {
|
||||
let yellow = Color.manaHex(0xFFB700)
|
||||
// Roundtrip ist nicht trivial, aber wir prüfen Existenz
|
||||
let _ = yellow
|
||||
let _ = ManaBrand.manaYellow
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Helpers
|
||||
|
||||
private func rgbComponents(of color: PlatformColor) -> (r: Double, g: Double, b: Double) {
|
||||
#if canImport(UIKit)
|
||||
var r: CGFloat = 0, g: CGFloat = 0, b: CGFloat = 0, a: CGFloat = 0
|
||||
color.getRed(&r, green: &g, blue: &b, alpha: &a)
|
||||
return (Double(r), Double(g), Double(b))
|
||||
#else
|
||||
let space = color.usingColorSpace(.deviceRGB) ?? color
|
||||
return (Double(space.redComponent), Double(space.greenComponent), Double(space.blueComponent))
|
||||
#endif
|
||||
}
|
||||
|
||||
private func approxEqual(_ a: Double, _ b: Double, tolerance: Double = 0.001) -> Bool {
|
||||
abs(a - b) < tolerance
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue