package server import ( "context" "encoding/json" "io" "net/http" "net/http/httptest" "strings" "testing" ) func TestPorkbunDNS(t *testing.T) { var createPath, createBody, deletePath string srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { body, _ := io.ReadAll(r.Body) switch { case strings.HasPrefix(r.URL.Path, "/dns/create/"): createPath, createBody = r.URL.Path, string(body) w.Write([]byte(`{"status":"SUCCESS","id":"42"}`)) case strings.HasPrefix(r.URL.Path, "/dns/deleteByNameType/"): deletePath = r.URL.Path w.Write([]byte(`{"status":"SUCCESS"}`)) default: http.NotFound(w, r) } })) defer srv.Close() p := &PorkbunDNS{APIKey: "pk", SecretKey: "sk", Zone: "tzgyn.dev", BaseURL: srv.URL, HTTP: srv.Client()} ctx := context.Background() record := "_acme-challenge.abc123.tunnel.tzgyn.dev" if err := p.CreateTXT(ctx, record, "challenge-value"); err != nil { t.Fatal(err) } if createPath != "/dns/create/tzgyn.dev" { t.Errorf("create path = %q", createPath) } var body map[string]string if err := json.Unmarshal([]byte(createBody), &body); err != nil { t.Fatal(err) } for key, want := range map[string]string{ "apikey": "pk", "secretapikey": "sk", "name": "_acme-challenge.abc123.tunnel", "type": "TXT", "content": "challenge-value", } { if body[key] != want { t.Errorf("create body %s = %q, want %q", key, body[key], want) } } if err := p.DeleteTXT(ctx, record); err != nil { t.Fatal(err) } if deletePath != "/dns/deleteByNameType/tzgyn.dev/TXT/_acme-challenge.abc123.tunnel" { t.Errorf("delete path = %q", deletePath) } // A record outside the zone fails before any request. if err := p.CreateTXT(ctx, "other.example.com", "v"); err == nil { t.Error("expected an error for a record outside the zone") } // API errors surface. bad := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Write([]byte(`{"status":"ERROR","message":"nope"}`)) })) defer bad.Close() p2 := &PorkbunDNS{APIKey: "pk", SecretKey: "sk", Zone: "tzgyn.dev", BaseURL: bad.URL, HTTP: bad.Client()} if err := p2.CreateTXT(ctx, record, "v"); err == nil || !strings.Contains(err.Error(), "nope") { t.Errorf("expected the API error, got %v", err) } } func TestPorkbunSweepChallenges(t *testing.T) { var deletes []string srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch { case r.URL.Path == "/dns/retrieve/tzgyn.dev": w.Write([]byte(`{"status":"SUCCESS","records":[ {"name":"_acme-challenge.a.tunnel.tzgyn.dev","type":"TXT","content":"one"}, {"name":"_acme-challenge.a.tunnel.tzgyn.dev","type":"TXT","content":"two"}, {"name":"_acme-challenge.b.tunnel.tzgyn.dev","type":"TXT","content":"three"}, {"name":"www.tzgyn.dev","type":"TXT","content":"keep"}, {"name":"_acme-challenge.other.example.com","type":"TXT","content":"outside"} ]}`)) case strings.HasPrefix(r.URL.Path, "/dns/deleteByNameType/"): deletes = append(deletes, r.URL.Path) w.Write([]byte(`{"status":"SUCCESS"}`)) default: http.NotFound(w, r) } })) defer srv.Close() p := &PorkbunDNS{APIKey: "pk", SecretKey: "sk", Zone: "tzgyn.dev", BaseURL: srv.URL, HTTP: srv.Client()} count, err := p.SweepChallenges(context.Background()) if err != nil { t.Fatal(err) } if count != 2 { t.Errorf("count = %d, want 2", count) } want := []string{ "/dns/deleteByNameType/tzgyn.dev/TXT/_acme-challenge.a.tunnel", "/dns/deleteByNameType/tzgyn.dev/TXT/_acme-challenge.b.tunnel", } if len(deletes) != len(want) { t.Fatalf("deletes = %v, want %v", deletes, want) } for i := range want { if deletes[i] != want[i] { t.Errorf("delete[%d] = %q, want %q", i, deletes[i], want[i]) } } }