feat: Implement configuration management and DNS provider integration
- Added configuration management using Viper in internal/config/config.go - Implemented ClientConfig, ServerConfig, TLSConfig, HetznerConfig, UpstreamConfig, and main Config struct. - Created LoadConfig function to read and validate configuration files. - Developed Hetzner DNS provider in internal/provider/hetzner/hetzner.go with methods for updating DNS records. - Added comprehensive unit tests for configuration loading and Hetzner provider functionality. - Established HTTP server with metrics and update endpoint in internal/server/server.go. - Implemented request handling, authorization, and error management in the server. - Created integration tests for the Hetzner provider API interactions. - Removed legacy dynamic DNS integration tests in favor of the new API-based approach.
This commit is contained in:
91
internal/provider/hetzner/hetzner_api_test.go
Normal file
91
internal/provider/hetzner/hetzner_api_test.go
Normal file
@@ -0,0 +1,91 @@
|
||||
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 TestUpdateRecordRecordNotFound(t *testing.T) {
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.URL.Path {
|
||||
case "/zones":
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"zones": []map[string]string{{"id": "z", "name": "example.com"}},
|
||||
})
|
||||
case "/records":
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{"records": []map[string]string{}})
|
||||
default:
|
||||
http.Error(w, "not found", http.StatusNotFound)
|
||||
}
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
provider := hetzner.NewProviderWithURL("token", ts.URL)
|
||||
err := provider.UpdateRecord(context.Background(), "missing.example.com", "1.1.1.1")
|
||||
if err == nil || !strings.Contains(err.Error(), "record missing.example.com not found") {
|
||||
t.Fatalf("expected record not found error, got %v", err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user