summaryrefslogtreecommitdiff
path: root/tests/FundLab.Api.Tests/ApiTests.fs
blob: 1aaab6b75528068f7712db9265c32fe68d6f8d12 (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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
namespace FundLab.Api.Tests

module ApiTests =

    open System
    open Xunit
    open FundLab.Api

    [<Theory>]
    [<InlineData("")>]
    [<InlineData("Basic secret")>]
    [<InlineData("Bearer wrong")>]
    let ``missing or wrong bearer token is unauthorized`` header =
        Assert.Equal(AuthDecision.Unauthorized, Authentication.authorize "secret" header)

    [<Fact>]
    let ``matching bearer token is authorized`` () =
        Assert.Equal(AuthDecision.Authorized, Authentication.authorize "secret" "Bearer secret")

    [<Fact>]
    let ``health payload is non-sensitive`` () =
        let payload = Health.payload "fund-lab-api"

        Assert.Contains("\"status\":\"ok\"", payload)
        Assert.DoesNotContain("secret", payload)

    [<Fact>]
    let ``AKShare NAV payload keeps missing publication time empty`` () =
        let payload =
            """
            {
              "schema_version": "fund-lab.akshare.v1",
              "operation": "nav",
              "source": "akshare",
              "source_revision": "akshare-1.18.96/eastmoney",
              "collected_at": "2026-09-21T10:00:00Z",
              "instrument": { "code": "000001" },
              "observations": [
                {
                  "nav_date": "2001-12-18",
                  "published_at": null,
                  "nav": "1.00000000",
                  "accumulated_nav": null,
                  "daily_return": "0.00"
                }
              ]
            }
            """

        match MarketData.parseNavPayload payload with
        | Ok parsed ->
            let observation = parsed.Observations |> List.head
            Assert.Equal("000001", parsed.Code)
            Assert.Equal(DateOnly(2001, 12, 18), observation.NavDate)
            Assert.Equal(None, observation.PublishedAt)
            Assert.Equal(None, observation.AccumulatedNav)
            Assert.Equal(Some 0.00m, observation.DailyReturn)
            Assert.Equal(1.00000000m, observation.Nav)
        | Error message ->
            Assert.Fail(sprintf "expected valid NAV payload, got %s" message)

    [<Fact>]
    let ``AKShare search payload preserves exact fund code and source`` () =
        let payload =
            """
            {
              "schema_version": "fund-lab.akshare.v1",
              "operation": "search",
              "source": "akshare",
              "source_revision": "akshare-1.18.96/eastmoney",
              "collected_at": "2026-09-21T10:00:00Z",
              "instruments": [
                {
                  "code": "000001",
                  "name": "华夏成长混合",
                  "fund_type": "混合型-灵活"
                }
              ]
            }
            """

        match MarketData.parseSearchPayload payload with
        | Ok parsed ->
            let instrument = parsed.Instruments |> List.head
            Assert.Equal("000001", instrument.Code)
            Assert.Equal("华夏成长混合", instrument.Name)
            Assert.Equal(Some "混合型-灵活", instrument.FundType)
            Assert.Equal("akshare", parsed.Source)
        | Error message ->
            Assert.Fail(sprintf "expected valid search payload, got %s" message)