129 lines
4.1 KiB
Go
129 lines
4.1 KiB
Go
package hetzner_test
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.cloonar.com/cloonar/updns/internal/provider/hetzner"
|
|
)
|
|
|
|
func TestUpdateRecordFullAPILifecycle(t *testing.T) {
|
|
domain := "test.example.com"
|
|
ip := "1.2.3.4"
|
|
zoneName := "example.com"
|
|
zoneID := "zone-123"
|
|
recID := "rec-456"
|
|
|
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
switch {
|
|
case r.Method == http.MethodGet && r.URL.Path == "/zones":
|
|
// Query zones by name
|
|
resp := map[string]interface{}{
|
|
"zones": []map[string]string{{"id": zoneID, "name": zoneName}},
|
|
}
|
|
json.NewEncoder(w).Encode(resp)
|
|
case r.Method == http.MethodGet && r.URL.Path == "/records":
|
|
// Query records by zone_id
|
|
resp := map[string]interface{}{
|
|
"records": []map[string]string{{"id": recID, "name": domain, "value": "0.0.0.0", "type": "A"}},
|
|
}
|
|
json.NewEncoder(w).Encode(resp)
|
|
case r.Method == http.MethodPut && r.URL.Path == "/records/"+recID:
|
|
// Validate update payload
|
|
body, _ := io.ReadAll(r.Body)
|
|
var payload map[string]string
|
|
json.Unmarshal(body, &payload)
|
|
if payload["value"] != ip {
|
|
t.Errorf("expected update value %s, got %s", ip, payload["value"])
|
|
}
|
|
w.WriteHeader(http.StatusOK)
|
|
default:
|
|
http.Error(w, "not found", http.StatusNotFound)
|
|
}
|
|
}))
|
|
defer ts.Close()
|
|
|
|
provider := hetzner.NewProviderWithURL("token", ts.URL)
|
|
if err := provider.UpdateRecord(context.Background(), domain, ip); err != nil {
|
|
t.Fatalf("full API lifecycle failed: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestUpdateRecordZoneNotFound(t *testing.T) {
|
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
json.NewEncoder(w).Encode(map[string]interface{}{"zones": []map[string]string{}})
|
|
}))
|
|
defer ts.Close()
|
|
|
|
provider := hetzner.NewProviderWithURL("token", ts.URL)
|
|
err := provider.UpdateRecord(context.Background(), "nozone.example", "1.1.1.1")
|
|
if err == nil || !strings.Contains(err.Error(), "zone example not found") {
|
|
t.Fatalf("expected zone not found error, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestUpdateRecordRecordNotFoundCreates(t *testing.T) {
|
|
domain := "new.example.com"
|
|
ip := "1.1.1.1"
|
|
zoneName := "example.com"
|
|
zoneID := "zone-abc"
|
|
postCalled := false
|
|
|
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
switch {
|
|
case r.Method == http.MethodGet && r.URL.Path == "/zones":
|
|
// Find zone
|
|
resp := map[string]interface{}{
|
|
"zones": []map[string]string{{"id": zoneID, "name": zoneName}},
|
|
}
|
|
json.NewEncoder(w).Encode(resp)
|
|
case r.Method == http.MethodGet && r.URL.Path == "/records":
|
|
// Find no records
|
|
resp := map[string]interface{}{
|
|
"records": []map[string]string{},
|
|
}
|
|
json.NewEncoder(w).Encode(resp)
|
|
case r.Method == http.MethodPost && r.URL.Path == "/records":
|
|
// Expect creation
|
|
postCalled = true
|
|
body, _ := io.ReadAll(r.Body)
|
|
var payload map[string]interface{} // Use interface{} for mixed types (TTL is int)
|
|
json.Unmarshal(body, &payload)
|
|
if payload["name"] != "new" { // Name should be the subdomain part
|
|
t.Errorf("expected create name 'new', got %s", payload["name"])
|
|
}
|
|
if payload["value"] != ip {
|
|
t.Errorf("expected create value %s, got %s", ip, payload["value"])
|
|
}
|
|
if payload["type"] != "A" {
|
|
t.Errorf("expected create type 'A', got %s", payload["type"])
|
|
}
|
|
if payload["zone_id"] != zoneID {
|
|
t.Errorf("expected create zone_id %s, got %s", zoneID, payload["zone_id"])
|
|
}
|
|
// Respond with created record details (optional, but good practice)
|
|
w.WriteHeader(http.StatusCreated)
|
|
json.NewEncoder(w).Encode(map[string]interface{}{"record": payload})
|
|
default:
|
|
t.Errorf("unexpected request: %s %s", r.Method, r.URL.Path)
|
|
http.Error(w, "unexpected request", http.StatusInternalServerError)
|
|
}
|
|
}))
|
|
defer ts.Close()
|
|
|
|
provider := hetzner.NewProviderWithURL("token", ts.URL)
|
|
err := provider.UpdateRecord(context.Background(), domain, ip)
|
|
if err != nil {
|
|
t.Fatalf("expected successful creation, but got error: %v", err)
|
|
}
|
|
if !postCalled {
|
|
t.Fatalf("expected POST /records to be called for creation, but it wasn't")
|
|
}
|
|
}
|