summaryrefslogtreecommitdiff
path: root/tests/SomhairlesDream.Shared.Tests/ArtifactContractTests.fs
blob: e4f0c864a63ccadd424029e69d3d0642047cecfe (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
module SomhairlesDream.Shared.Tests.ArtifactContractTests

open System
open Xunit
open SomhairlesDream.Shared

let private ids : RunIds =
    { ProjectId = "heritage-001"
      RunId = "run-20260920-001"
      BaseId = "heritage"
      TargetId = "heritage-001" }

[<Fact>]
let ``resource ids reject path traversal and separators`` () =
    Assert.True(Ids.validateResourceId "heritage-001" |> Result.isOk)
    Assert.True(Ids.validateResourceId "../outside" |> Result.isError)
    Assert.True(Ids.validateResourceId "heritage/001" |> Result.isError)
    Assert.True(Ids.validateResourceId "heritage_001" |> Result.isError)

[<Fact>]
let ``object ids use the stable dotted namespace`` () =
    Assert.True(Ids.validateObjectId "heritage.foundation" |> Result.isOk)
    Assert.True(Ids.validateObjectId "heritage/frame.left" |> Result.isError)
    Assert.True(Ids.validateObjectId "heritage.foundation/../../outside" |> Result.isError)

[<Fact>]
let ``start event serializes as flat camel-case json`` () =
    let event =
        RunEvent.Start
            { Ids = ids
              At = DateTimeOffset.Parse("2026-09-20T12:00:00Z")
              TotalSteps = 3 }

    let json = RunEvent.toJson event

    Assert.Contains("\"kind\":\"start\"", json)
    Assert.Contains("\"projectId\":\"heritage-001\"", json)
    Assert.Contains("\"runId\":\"run-20260920-001\"", json)
    Assert.Contains("\"totalSteps\":3", json)
    Assert.DoesNotContain("\"stepId\":null", json)

[<Fact>]
let ``checkpoint event includes its step and artifact path`` () =
    let event =
        RunEvent.Checkpoint
            { Ids = ids
              At = DateTimeOffset.Parse("2026-09-20T12:00:05Z")
              StepId = "01-foundation"
              StepIndex = 1
              TotalSteps = 3
              Message = "foundation exported"
              ArtifactPath = "steps/01-foundation.glb" }

    let wire = RunEvent.toWire event

    Assert.Equal("checkpoint", wire.Kind)
    Assert.Equal(Some "01-foundation", wire.StepId)
    Assert.Equal(Some 3, wire.TotalSteps)
    Assert.Equal(Some "steps/01-foundation.glb", wire.ArtifactPath)

[<Fact>]
let ``checkpoint event serializes total steps`` () =
    let event =
        RunEvent.Checkpoint
            { Ids = ids
              At = DateTimeOffset.Parse("2026-09-20T12:00:05Z")
              StepId = "01-foundation"
              StepIndex = 0
              TotalSteps = 3
              Message = "foundation exported"
              ArtifactPath = "steps/01-foundation.glb" }

    Assert.Contains("\"totalSteps\":3", RunEvent.toJson event)