280 lines
11 KiB
Swift
280 lines
11 KiB
Swift
import CodexBarCore
|
|
import Foundation
|
|
import Testing
|
|
@testable import CodexBar
|
|
|
|
struct UsageStorePlanUtilizationResetCoalescingTests {
|
|
@Test
|
|
func `near canonical codex windows merge into canonical history series`() throws {
|
|
let base = Date(timeIntervalSince1970: 1_700_000_000)
|
|
let existing = [
|
|
planSeries(name: .session, windowMinutes: 300, entries: [
|
|
planEntry(at: base, usedPercent: 20),
|
|
]),
|
|
planSeries(name: .weekly, windowMinutes: 10080, entries: [
|
|
planEntry(at: base, usedPercent: 40),
|
|
]),
|
|
]
|
|
let incoming = [
|
|
planSeries(name: .session, windowMinutes: 299, entries: [
|
|
planEntry(at: base.addingTimeInterval(3600), usedPercent: 30),
|
|
]),
|
|
planSeries(name: .weekly, windowMinutes: 10079, entries: [
|
|
planEntry(at: base.addingTimeInterval(3600), usedPercent: 50),
|
|
]),
|
|
]
|
|
|
|
let updated = try #require(
|
|
UsageStore._updatedPlanUtilizationHistoriesForTesting(
|
|
existingHistories: existing,
|
|
samples: incoming))
|
|
|
|
#expect(updated.map { "\($0.name.rawValue):\($0.windowMinutes)" } == ["session:300", "weekly:10080"])
|
|
#expect(findSeries(updated, name: .session, windowMinutes: 300)?.entries.map(\.usedPercent) == [20, 30])
|
|
#expect(findSeries(updated, name: .weekly, windowMinutes: 10080)?.entries.map(\.usedPercent) == [40, 50])
|
|
}
|
|
|
|
@Test
|
|
func `same hour entry backfills missing reset metadata`() throws {
|
|
let calendar = Calendar(identifier: .gregorian)
|
|
let hourStart = try #require(calendar.date(from: DateComponents(
|
|
timeZone: TimeZone(secondsFromGMT: 0),
|
|
year: 2026,
|
|
month: 3,
|
|
day: 17,
|
|
hour: 9)))
|
|
let existing = planEntry(
|
|
at: hourStart.addingTimeInterval(10 * 60),
|
|
usedPercent: 20)
|
|
let incoming = planEntry(
|
|
at: hourStart.addingTimeInterval(45 * 60),
|
|
usedPercent: 30,
|
|
resetsAt: hourStart.addingTimeInterval(30 * 60))
|
|
|
|
let updated = try #require(
|
|
UsageStore._updatedPlanUtilizationEntriesForTesting(
|
|
existingEntries: [existing],
|
|
entry: incoming))
|
|
|
|
#expect(updated.count == 1)
|
|
#expect(updated[0].capturedAt == incoming.capturedAt)
|
|
#expect(updated[0].usedPercent == 30)
|
|
#expect(updated[0].resetsAt == incoming.resetsAt)
|
|
}
|
|
|
|
@Test
|
|
func `same hour later higher usage without reset metadata keeps promoted reset boundary`() throws {
|
|
let calendar = Calendar(identifier: .gregorian)
|
|
let hourStart = try #require(calendar.date(from: DateComponents(
|
|
timeZone: TimeZone(secondsFromGMT: 0),
|
|
year: 2026,
|
|
month: 3,
|
|
day: 17,
|
|
hour: 9)))
|
|
let first = planEntry(
|
|
at: hourStart.addingTimeInterval(10 * 60),
|
|
usedPercent: 40)
|
|
let second = planEntry(
|
|
at: hourStart.addingTimeInterval(25 * 60),
|
|
usedPercent: 8,
|
|
resetsAt: hourStart.addingTimeInterval(30 * 60))
|
|
let third = planEntry(
|
|
at: hourStart.addingTimeInterval(50 * 60),
|
|
usedPercent: 22)
|
|
|
|
let initial = try #require(
|
|
UsageStore._updatedPlanUtilizationEntriesForTesting(
|
|
existingEntries: [],
|
|
entry: first))
|
|
let promoted = try #require(
|
|
UsageStore._updatedPlanUtilizationEntriesForTesting(
|
|
existingEntries: initial,
|
|
entry: second))
|
|
let updated = try #require(
|
|
UsageStore._updatedPlanUtilizationEntriesForTesting(
|
|
existingEntries: promoted,
|
|
entry: third))
|
|
|
|
#expect(updated.count == 1)
|
|
#expect(updated[0].capturedAt == third.capturedAt)
|
|
#expect(updated[0].usedPercent == third.usedPercent)
|
|
#expect(updated[0].resetsAt == second.resetsAt)
|
|
}
|
|
|
|
@Test
|
|
func `same hour zero usage with drifting reset coalesces to latest entry`() throws {
|
|
let calendar = Calendar(identifier: .gregorian)
|
|
let hourStart = try #require(calendar.date(from: DateComponents(
|
|
timeZone: TimeZone(secondsFromGMT: 0),
|
|
year: 2026,
|
|
month: 3,
|
|
day: 20,
|
|
hour: 0)))
|
|
let existing = planEntry(
|
|
at: hourStart.addingTimeInterval(14 * 60),
|
|
usedPercent: 0,
|
|
resetsAt: hourStart.addingTimeInterval(5 * 60 * 60 + 14 * 60 + 2))
|
|
let incoming = planEntry(
|
|
at: hourStart.addingTimeInterval(23 * 60),
|
|
usedPercent: 0,
|
|
resetsAt: hourStart.addingTimeInterval(5 * 60 * 60 + 14 * 60 + 3))
|
|
|
|
let updated = try #require(
|
|
UsageStore._updatedPlanUtilizationEntriesForTesting(
|
|
existingEntries: [existing],
|
|
entry: incoming))
|
|
|
|
#expect(updated.count == 1)
|
|
#expect(updated[0] == incoming)
|
|
}
|
|
|
|
@Test
|
|
func `same hour reset times within two minutes still keep single hourly peak`() throws {
|
|
let calendar = Calendar(identifier: .gregorian)
|
|
let hourStart = try #require(calendar.date(from: DateComponents(
|
|
timeZone: TimeZone(secondsFromGMT: 0),
|
|
year: 2026,
|
|
month: 3,
|
|
day: 20,
|
|
hour: 0)))
|
|
let existing = planEntry(
|
|
at: hourStart.addingTimeInterval(21 * 60),
|
|
usedPercent: 10,
|
|
resetsAt: hourStart.addingTimeInterval(3 * 60 * 60))
|
|
let incoming = planEntry(
|
|
at: hourStart.addingTimeInterval(55 * 60),
|
|
usedPercent: 10,
|
|
resetsAt: hourStart.addingTimeInterval(3 * 60 * 60 + 1))
|
|
|
|
let updated = try #require(
|
|
UsageStore._updatedPlanUtilizationEntriesForTesting(
|
|
existingEntries: [existing],
|
|
entry: incoming))
|
|
|
|
#expect(updated.count == 1)
|
|
#expect(updated[0].capturedAt == incoming.capturedAt)
|
|
#expect(updated[0].usedPercent == 10)
|
|
#expect(updated[0].resetsAt == incoming.resetsAt)
|
|
}
|
|
|
|
@Test
|
|
func `same hour usage drop without meaningful reset still keeps single hourly peak`() throws {
|
|
let calendar = Calendar(identifier: .gregorian)
|
|
let hourStart = try #require(calendar.date(from: DateComponents(
|
|
timeZone: TimeZone(secondsFromGMT: 0),
|
|
year: 2026,
|
|
month: 3,
|
|
day: 20,
|
|
hour: 0)))
|
|
let existing = planEntry(
|
|
at: hourStart.addingTimeInterval(15 * 60),
|
|
usedPercent: 40,
|
|
resetsAt: hourStart.addingTimeInterval(3 * 60 * 60))
|
|
let incoming = planEntry(
|
|
at: hourStart.addingTimeInterval(45 * 60),
|
|
usedPercent: 5,
|
|
resetsAt: hourStart.addingTimeInterval(3 * 60 * 60 + 30))
|
|
|
|
let updated = try #require(
|
|
UsageStore._updatedPlanUtilizationEntriesForTesting(
|
|
existingEntries: [existing],
|
|
entry: incoming))
|
|
|
|
#expect(updated.count == 1)
|
|
#expect(updated[0].capturedAt == existing.capturedAt)
|
|
#expect(updated[0].usedPercent == existing.usedPercent)
|
|
#expect(updated[0].resetsAt == incoming.resetsAt)
|
|
}
|
|
|
|
@Test
|
|
func `same hour reset keeps peak before reset and latest peak after reset`() throws {
|
|
let calendar = Calendar(identifier: .gregorian)
|
|
let hourStart = try #require(calendar.date(from: DateComponents(
|
|
timeZone: TimeZone(secondsFromGMT: 0),
|
|
year: 2026,
|
|
month: 3,
|
|
day: 20,
|
|
hour: 0)))
|
|
let initial = [
|
|
planEntry(
|
|
at: hourStart.addingTimeInterval(5 * 60),
|
|
usedPercent: 40,
|
|
resetsAt: hourStart.addingTimeInterval(3 * 60 * 60)),
|
|
planEntry(
|
|
at: hourStart.addingTimeInterval(20 * 60),
|
|
usedPercent: 12,
|
|
resetsAt: hourStart.addingTimeInterval(8 * 60 * 60)),
|
|
]
|
|
let incoming = planEntry(
|
|
at: hourStart.addingTimeInterval(45 * 60),
|
|
usedPercent: 18,
|
|
resetsAt: hourStart.addingTimeInterval(8 * 60 * 60))
|
|
|
|
let updated = try #require(
|
|
UsageStore._updatedPlanUtilizationEntriesForTesting(
|
|
existingEntries: initial,
|
|
entry: incoming))
|
|
|
|
#expect(updated.count == 2)
|
|
#expect(updated[0].usedPercent == 40)
|
|
#expect(updated[1].usedPercent == 18)
|
|
#expect(updated[1].resetsAt == incoming.resetsAt)
|
|
}
|
|
|
|
@Test
|
|
func `newer reset within hour replaces earlier post reset record`() throws {
|
|
let calendar = Calendar(identifier: .gregorian)
|
|
let hourStart = try #require(calendar.date(from: DateComponents(
|
|
timeZone: TimeZone(secondsFromGMT: 0),
|
|
year: 2026,
|
|
month: 3,
|
|
day: 20,
|
|
hour: 0)))
|
|
let initial = [
|
|
planEntry(
|
|
at: hourStart.addingTimeInterval(5 * 60),
|
|
usedPercent: 40,
|
|
resetsAt: hourStart.addingTimeInterval(3 * 60 * 60)),
|
|
planEntry(
|
|
at: hourStart.addingTimeInterval(20 * 60),
|
|
usedPercent: 12,
|
|
resetsAt: hourStart.addingTimeInterval(8 * 60 * 60)),
|
|
]
|
|
let incoming = planEntry(
|
|
at: hourStart.addingTimeInterval(50 * 60),
|
|
usedPercent: 3,
|
|
resetsAt: hourStart.addingTimeInterval(10 * 60 * 60))
|
|
|
|
let updated = try #require(
|
|
UsageStore._updatedPlanUtilizationEntriesForTesting(
|
|
existingEntries: initial,
|
|
entry: incoming))
|
|
|
|
#expect(updated.count == 2)
|
|
#expect(updated[0].usedPercent == 40)
|
|
#expect(updated[1] == incoming)
|
|
}
|
|
|
|
@Test
|
|
func `merged histories keep series separated by stable name`() throws {
|
|
let existing = [
|
|
planSeries(name: .session, windowMinutes: 300, entries: [
|
|
planEntry(at: Date(timeIntervalSince1970: 1_700_000_000), usedPercent: 20),
|
|
]),
|
|
]
|
|
let incoming = [
|
|
planSeries(name: .weekly, windowMinutes: 10080, entries: [
|
|
planEntry(at: Date(timeIntervalSince1970: 1_700_000_000), usedPercent: 40),
|
|
]),
|
|
]
|
|
|
|
let updated = try #require(
|
|
UsageStore._updatedPlanUtilizationHistoriesForTesting(
|
|
existingHistories: existing,
|
|
samples: incoming))
|
|
|
|
#expect(findSeries(updated, name: .session, windowMinutes: 300) != nil)
|
|
#expect(findSeries(updated, name: .weekly, windowMinutes: 10080) != nil)
|
|
}
|
|
}
|