api-swift / Tests / IrdAPITests / Gate / GatedOperationsTests.swift
621 words
  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
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
import Foundation
import Testing
@testable import IrdAPI

@Suite struct GatedOperationsTests {
    /// The gated mutations of the sibling TypeScript client, verbatim.
    static let expected: Set<String> = [
        "POST /domains/registrations", "POST /domains/transfers-in", "POST /domains/trades-in", "POST /domains/adoptions",
        "PUT /dns/domains/{domain}/records", "DELETE /dns/domains/{domain}/records", "POST /dns/claims",
        "POST /tunnels", "POST /ipfs/domains", "POST /p2p/apps/{id}/domains", "POST /vps/instances/{id}/domains",
        "POST /domains/registrations/{id}/renew", "PUT /domains/registrations/{id}/auto-renew", "PUT /domains/registrations/{id}/sensitive",
        "PUT /domains/registrations/{id}/nameservers", "POST /domains/registrations/{id}/hosts", "PUT /domains/registrations/{id}/hosts/{host}",
        "DELETE /domains/registrations/{id}/hosts/{host}", "POST /domains/registrations/{id}/dsdata", "DELETE /domains/registrations/{id}/dsdata",
        "POST /domains/registrations/{id}/transfer-out", "PUT /domains/registrations/{id}/registrant",
        "POST /domains/registrations/{id}/registrant-email/start", "POST /domains/registrations/{id}/registrant-email/confirm",
        "POST /domains/registrations/{id}/registrant-email/adopt", "POST /domains/registrations/{id}/trade",
        "POST /domains/registrations/{id}/dns", "DELETE /domains/registrations/{id}/dns", "POST /domains/registrations/{id}/dns/dnssec",
        "DELETE /domains/registrations/{id}/dns/dnssec", "POST /domains/registrations/{id}/dns/nameservers",
        "POST /domains/registrations/{id}/grants", "DELETE /domains/registrations/{id}/grants/{grant-id}",
        "POST /domains/actions/{id}/cancel", "DELETE /dns/claims/{id}", "PUT /dns/claims/{id}/seed", "DELETE /dns/claims/{id}/seed",
        "POST /ipfs/domains/{id}/pins", "DELETE /ipfs/domains/{id}", "DELETE /p2p/apps/{id}/domains/{domain-id}",
        "DELETE /tunnels/{id}", "PATCH /tunnels/{id}", "DELETE /vps/instances/{id}/domains/{connection-id}",
    ]

    @Test func tableMatchesTheSiblingClientsAndTheSpec() {
        let keys = Set(GatedOperations.table.keys)
        #expect(keys == Self.expected, "\(keys.symmetricDifference(Self.expected).sorted())")
        #expect(keys.count == 43)
        let spec = Spec.operations
        for k in keys {
            #expect(spec.contains(k), "\(k) is not a spec operation")
            #expect(!k.hasPrefix("GET "), "reads are never gated: \(k)")
        }
        for case .resolve(let r) in GatedOperations.table.values {
            #expect(spec.contains(r.read.key), "resolve read \(r.read.key) is not a spec operation")
        }
    }

    @Test func gatewayDomainMutationsResolveThenCheck() async throws {
        let (client, http) = try await gatedClient(rules: GateRules(deniedDomains: ["gw.x.ro"]),
                                                   script: [json(200, #"{"domain":{"id":"gw-1","domain":"gw.x.ro"}}"#)])
        let start = http.count
        let e = await caught { _ = try await client.raw.request(method: .post, path: "/ipfs/domains/{id}/pins", params: ["id": "gw-1"], body: ["add": ["p2"]]) }
        #expect(e == .domainDenied(domain: "gw.x.ro", pattern: "gw.x.ro", op: "POST /ipfs/domains/{id}/pins"))
        #expect(Array(http.trail[start...]) == ["GET /ipfs/domains/gw-1"])

        let (ok, http2) = try await gatedClient(rules: GateRules(deniedDomains: ["other.ro"]),
                                                script: [json(200, #"{"domain":{"id":"gw-1","domain":"gw.x.ro"}}"#), json(200, #"{"domain":{"id":"gw-1"}}"#)])
        let s2 = http2.count
        _ = try await ok.raw.request(method: .post, path: "/ipfs/domains/{id}/pins", params: ["id": "gw-1"], body: ["add": ["p2"]])
        #expect(Array(http2.trail[s2...]) == ["GET /ipfs/domains/gw-1", "POST /ipfs/domains/gw-1/pins"])
        #expect(bodyJSON(http2.last) == ["add": ["p2"]])
    }

    @Test func claimMutationsResolveThroughTheClaim() async throws {
        let (client, http) = try await gatedClient(rules: GateRules(allowedDomains: ["example.com"]),
                                                   script: [json(200, #"{"claim":{"id":"c1","domain":"example.com"}}"#), json(200, "{}")])
        let s = http.count
        _ = try await client.raw.request(method: .put, path: "/dns/claims/{id}/seed", params: ["id": "c1"], body: ["snapshot_id": "s1"])
        #expect(Array(http.trail[s...]) == ["GET /dns/claims/c1", "PUT /dns/claims/c1/seed"])

        let (denied, http2) = try await gatedClient(rules: GateRules(allowedDomains: ["example.com"]),
                                                    script: [json(200, #"{"claim":{"id":"c2","domain":"other.com"}}"#)])
        let s2 = http2.count
        let e = await caught { _ = try await denied.raw.request(method: .delete, path: "/dns/claims/{id}", params: ["id": "c2"]) }
        #expect(e == .domainDenied(domain: "other.com", pattern: nil, op: "DELETE /dns/claims/{id}"))
        #expect(Array(http2.trail[s2...]) == ["GET /dns/claims/c2"])
    }

    @Test func cancelActionResolvesTheRegistrationId() async throws {
        let (client, http) = try await gatedClient(rules: GateRules(deniedDomains: ["prod.ro"]),
                                                   script: [json(200, #"{"action":{"id":"a1","registration_id":"r1"}}"#)])
        let s = http.count
        let e = await caught { _ = try await client.raw.request(method: .post, path: "/domains/actions/{id}/cancel", params: ["id": "a1"]) }
        #expect(e == .domainDenied(domain: "prod.ro", pattern: "prod.ro", op: "POST /domains/actions/{id}/cancel"))
        #expect(Array(http.trail[s...]) == ["GET /domains/actions/a1"])
    }

    @Test func detachRowsAreFoundByIdInTheListing() async throws {
        let (client, http) = try await gatedClient(rules: GateRules(deniedDomains: ["p2p.x.ro"]),
                                                   script: [json(200, #"{"domains":[{"id":"d0","hostname":"a.ro"},{"id":"d1","hostname":"p2p.x.ro"}]}"#)])
        let s = http.count
        let e = await caught { _ = try await client.raw.request(method: .delete, path: "/p2p/apps/{id}/domains/{domain-id}", params: ["id": "a1", "domain-id": "d1"]) }
        #expect(e == .domainDenied(domain: "p2p.x.ro", pattern: "p2p.x.ro", op: "DELETE /p2p/apps/{id}/domains/{domain-id}"))
        #expect(Array(http.trail[s...]) == ["GET /p2p/apps/a1/domains"])

        let (vps, http2) = try await gatedClient(rules: GateRules(deniedDomains: ["x.ro"]),
                                                 script: [json(200, #"{"connections":[{"id":"c1","zone_origin":"x.ro"}]}"#)])
        let e2 = await caught { _ = try await vps.raw.request(method: .delete, path: "/vps/instances/{id}/domains/{connection-id}", params: ["id": "i1", "connection-id": "c1"]) }
        #expect(e2 == .domainDenied(domain: "x.ro", pattern: "x.ro", op: "DELETE /vps/instances/{id}/domains/{connection-id}"))
        #expect(http2.trail.last == "GET /vps/instances/i1/domains")
    }

    @Test func pathParamAndBodyFieldSources() async throws {
        let (client, http) = try await gatedClient(rules: GateRules(deniedDomains: ["x.ro"]), script: [])
        let s = http.count
        let e = await caught { _ = try await client.raw.request(method: .put, path: "/dns/domains/{domain}/records", params: ["domain": "X.RO"], body: ["record": [:]]) }
        #expect(e == .domainDenied(domain: "x.ro", pattern: "x.ro", op: "PUT /dns/domains/{domain}/records"))
        let e2 = await caught { _ = try await client.raw.request(method: .post, path: "/vps/instances/{id}/domains", params: ["id": "i1"], body: ["zone": "x.ro"]) }
        #expect(e2 == .domainDenied(domain: "x.ro", pattern: "x.ro", op: "POST /vps/instances/{id}/domains"))
        #expect(http.count == s)
        _ = try await client.raw.request(method: .post, path: "/vps/instances/{id}/domains", params: ["id": "i1"], body: ["zone": "y.ro"])
        #expect(http.trail.last == "POST /vps/instances/i1/domains")
    }
}

static mirror of HEAD · about · clone: git clone https://ird.cod-sursa.ro/api-swift.git