mirror of
https://github.com/go-acme/lego
synced 2026-03-14 14:35:48 +01:00
test: server client mock (#2571)
This commit is contained in:
parent
fae73fdc5d
commit
52e167c930
275 changed files with 8834 additions and 10259 deletions
1
go.mod
1
go.mod
|
|
@ -35,6 +35,7 @@ require (
|
|||
github.com/exoscale/egoscale/v3 v3.1.13
|
||||
github.com/go-jose/go-jose/v4 v4.0.5
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1
|
||||
github.com/google/go-cmp v0.7.0
|
||||
github.com/google/go-querystring v1.1.0
|
||||
github.com/gophercloud/gophercloud v1.14.1
|
||||
github.com/gophercloud/utils v0.0.0-20231010081019-80377eca5d56
|
||||
|
|
|
|||
72
platform/tester/servermock/builder.go
Normal file
72
platform/tester/servermock/builder.go
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
package servermock
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"slices"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// Link represents a middleware interface, enabling middleware chaining.
|
||||
type Link interface {
|
||||
Bind(next http.Handler) http.Handler
|
||||
}
|
||||
|
||||
// LinkFunc defines a function type [Link].
|
||||
type LinkFunc func(next http.Handler) http.Handler
|
||||
|
||||
func (f LinkFunc) Bind(next http.Handler) http.Handler {
|
||||
return f(next)
|
||||
}
|
||||
|
||||
// ClientBuilder defines a function type for creating a client of type T based on a httptest.Server instance.
|
||||
type ClientBuilder[T any] func(server *httptest.Server) (T, error)
|
||||
|
||||
// Builder is a type that facilitates the construction of testable HTTP clients and server.
|
||||
// It allows defining routes, attaching middleware, and creating custom HTTP clients.
|
||||
type Builder[T any] struct {
|
||||
mux *http.ServeMux
|
||||
chain []Link
|
||||
|
||||
clientBuilder ClientBuilder[T]
|
||||
}
|
||||
|
||||
func NewBuilder[T any](clientBuilder ClientBuilder[T], chain ...Link) *Builder[T] {
|
||||
return &Builder[T]{
|
||||
mux: http.NewServeMux(),
|
||||
chain: chain,
|
||||
clientBuilder: clientBuilder,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Builder[T]) Route(pattern string, handler http.Handler, chain ...Link) *Builder[T] {
|
||||
if handler == nil {
|
||||
handler = Noop()
|
||||
}
|
||||
|
||||
for _, link := range slices.Backward(b.chain) {
|
||||
handler = link.Bind(handler)
|
||||
}
|
||||
|
||||
for _, link := range slices.Backward(chain) {
|
||||
handler = link.Bind(handler)
|
||||
}
|
||||
|
||||
b.mux.Handle(pattern, handler)
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *Builder[T]) Build(t *testing.T) T {
|
||||
t.Helper()
|
||||
|
||||
server := httptest.NewServer(b.mux)
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
client, err := b.clientBuilder(server)
|
||||
require.NoError(t, err)
|
||||
|
||||
return client
|
||||
}
|
||||
20
platform/tester/servermock/handler_dump.go
Normal file
20
platform/tester/servermock/handler_dump.go
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
package servermock
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
)
|
||||
|
||||
// DumpRequest logs the full HTTP request to the console, including the body if present.
|
||||
func DumpRequest() http.HandlerFunc {
|
||||
return func(rw http.ResponseWriter, req *http.Request) {
|
||||
dump, err := httputil.DumpRequest(req, true)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(string(dump))
|
||||
}
|
||||
}
|
||||
77
platform/tester/servermock/handler_file.go
Normal file
77
platform/tester/servermock/handler_file.go
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
package servermock
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
)
|
||||
|
||||
// ResponseFromFileHandler handles HTTP responses using the content of a file.
|
||||
type ResponseFromFileHandler struct {
|
||||
statusCode int
|
||||
headers http.Header
|
||||
filename string
|
||||
}
|
||||
|
||||
func ResponseFromFile(filename string) *ResponseFromFileHandler {
|
||||
return &ResponseFromFileHandler{
|
||||
statusCode: http.StatusOK,
|
||||
headers: http.Header{},
|
||||
filename: filename,
|
||||
}
|
||||
}
|
||||
|
||||
func ResponseFromFixture(filename string) *ResponseFromFileHandler {
|
||||
return ResponseFromFile(filepath.Join("fixtures", filename))
|
||||
}
|
||||
|
||||
func (h *ResponseFromFileHandler) ServeHTTP(rw http.ResponseWriter, _ *http.Request) {
|
||||
for k, values := range h.headers {
|
||||
for _, v := range values {
|
||||
rw.Header().Add(k, v)
|
||||
}
|
||||
}
|
||||
|
||||
if h.filename == "" {
|
||||
rw.WriteHeader(h.statusCode)
|
||||
return
|
||||
}
|
||||
|
||||
if filepath.Ext(h.filename) == ".json" {
|
||||
rw.Header().Set(contentTypeHeader, applicationJSONMimeType)
|
||||
}
|
||||
|
||||
file, err := os.Open(h.filename)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
defer func() { _ = file.Close() }()
|
||||
|
||||
rw.WriteHeader(h.statusCode)
|
||||
|
||||
_, err = io.Copy(rw, file)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (h *ResponseFromFileHandler) WithStatusCode(status int) *ResponseFromFileHandler {
|
||||
if h.statusCode >= http.StatusContinue {
|
||||
h.statusCode = status
|
||||
}
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
func (h *ResponseFromFileHandler) WithHeader(name, value string, values ...string) *ResponseFromFileHandler {
|
||||
for _, v := range slices.Concat([]string{value}, values) {
|
||||
h.headers.Add(name, v)
|
||||
}
|
||||
|
||||
return h
|
||||
}
|
||||
39
platform/tester/servermock/handler_json.go
Normal file
39
platform/tester/servermock/handler_json.go
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
package servermock
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// JSONEncodeHandler is a handler that encodes data into JSON and writes it to an HTTP response.
|
||||
type JSONEncodeHandler struct {
|
||||
data any
|
||||
statusCode int
|
||||
}
|
||||
|
||||
func JSONEncode(data any) *JSONEncodeHandler {
|
||||
return &JSONEncodeHandler{
|
||||
data: data,
|
||||
statusCode: http.StatusOK,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *JSONEncodeHandler) ServeHTTP(rw http.ResponseWriter, _ *http.Request) {
|
||||
rw.Header().Set(contentTypeHeader, applicationJSONMimeType)
|
||||
|
||||
rw.WriteHeader(h.statusCode)
|
||||
|
||||
err := json.NewEncoder(rw).Encode(h.data)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (h *JSONEncodeHandler) WithStatusCode(status int) *JSONEncodeHandler {
|
||||
if h.statusCode >= http.StatusContinue {
|
||||
h.statusCode = status
|
||||
}
|
||||
|
||||
return h
|
||||
}
|
||||
45
platform/tester/servermock/handler_noop.go
Normal file
45
platform/tester/servermock/handler_noop.go
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
package servermock
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"slices"
|
||||
)
|
||||
|
||||
// NoopHandler is a simple HTTP handler that responds without processing requests.
|
||||
type NoopHandler struct {
|
||||
statusCode int
|
||||
headers http.Header
|
||||
}
|
||||
|
||||
func Noop() *NoopHandler {
|
||||
return &NoopHandler{
|
||||
statusCode: http.StatusOK,
|
||||
headers: http.Header{},
|
||||
}
|
||||
}
|
||||
|
||||
func (h *NoopHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||
for k, values := range h.headers {
|
||||
for _, v := range values {
|
||||
rw.Header().Add(k, v)
|
||||
}
|
||||
}
|
||||
|
||||
rw.WriteHeader(h.statusCode)
|
||||
}
|
||||
|
||||
func (h *NoopHandler) WithStatusCode(status int) *NoopHandler {
|
||||
if h.statusCode >= http.StatusContinue {
|
||||
h.statusCode = status
|
||||
}
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
func (h *NoopHandler) WithHeader(name, value string, values ...string) *NoopHandler {
|
||||
for _, v := range slices.Concat([]string{value}, values) {
|
||||
h.headers.Add(name, v)
|
||||
}
|
||||
|
||||
return h
|
||||
}
|
||||
61
platform/tester/servermock/handler_raw.go
Normal file
61
platform/tester/servermock/handler_raw.go
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
package servermock
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"slices"
|
||||
)
|
||||
|
||||
// RawResponseHandler is a custom HTTP handler that serves raw response data.
|
||||
type RawResponseHandler struct {
|
||||
statusCode int
|
||||
headers http.Header
|
||||
data []byte
|
||||
}
|
||||
|
||||
func RawResponse(data []byte) *RawResponseHandler {
|
||||
return &RawResponseHandler{
|
||||
statusCode: http.StatusOK,
|
||||
headers: http.Header{},
|
||||
data: data,
|
||||
}
|
||||
}
|
||||
|
||||
func RawStringResponse(data string) *RawResponseHandler {
|
||||
return RawResponse([]byte(data))
|
||||
}
|
||||
|
||||
func (h *RawResponseHandler) ServeHTTP(rw http.ResponseWriter, _ *http.Request) {
|
||||
for k, values := range h.headers {
|
||||
for _, v := range values {
|
||||
rw.Header().Add(k, v)
|
||||
}
|
||||
}
|
||||
|
||||
rw.WriteHeader(h.statusCode)
|
||||
|
||||
if len(h.data) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
_, err := rw.Write(h.data)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (h *RawResponseHandler) WithStatusCode(status int) *RawResponseHandler {
|
||||
if h.statusCode >= http.StatusContinue {
|
||||
h.statusCode = status
|
||||
}
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
func (h *RawResponseHandler) WithHeader(name, value string, values ...string) *RawResponseHandler {
|
||||
for _, v := range slices.Concat([]string{value}, values) {
|
||||
h.headers.Add(name, v)
|
||||
}
|
||||
|
||||
return h
|
||||
}
|
||||
94
platform/tester/servermock/link_form.go
Normal file
94
platform/tester/servermock/link_form.go
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
package servermock
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"slices"
|
||||
)
|
||||
|
||||
// FormLink is a type used for validating and processing form data in HTTP requests.
|
||||
// It supports strict validation, predefined values, and regex-based checks to ensure form compliance.
|
||||
type FormLink struct {
|
||||
values url.Values
|
||||
regexes map[string]*regexp.Regexp
|
||||
strict bool
|
||||
usePostForm bool
|
||||
statusCode int
|
||||
}
|
||||
|
||||
func CheckForm() *FormLink {
|
||||
return &FormLink{
|
||||
values: url.Values{},
|
||||
regexes: map[string]*regexp.Regexp{},
|
||||
statusCode: http.StatusBadRequest,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *FormLink) Bind(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
err := req.ParseForm()
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), l.statusCode)
|
||||
return
|
||||
}
|
||||
|
||||
form := req.Form
|
||||
if l.usePostForm {
|
||||
form = req.PostForm
|
||||
}
|
||||
|
||||
if l.strict {
|
||||
if len(form) != len(l.values)+len(l.regexes) {
|
||||
msg := fmt.Sprintf("invalid query parameters, got %v, want %v", req.Form, l.values)
|
||||
http.Error(rw, msg, l.statusCode)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
for k, v := range l.values {
|
||||
value := form[k]
|
||||
if !slices.Equal(v, value) {
|
||||
msg := fmt.Sprintf("invalid %q form value, got %q, want %q", k, value, v)
|
||||
http.Error(rw, msg, l.statusCode)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
for k, exp := range l.regexes {
|
||||
value := form.Get(k)
|
||||
if !exp.MatchString(value) {
|
||||
msg := fmt.Sprintf("invalid %q form value, %q doesn't match to %q", k, value, exp)
|
||||
http.Error(rw, msg, l.statusCode)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
next.ServeHTTP(rw, req)
|
||||
})
|
||||
}
|
||||
|
||||
func (l *FormLink) Strict() *FormLink {
|
||||
l.strict = true
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *FormLink) UsePostForm() *FormLink {
|
||||
l.usePostForm = true
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *FormLink) With(name, value string) *FormLink {
|
||||
l.values.Set(name, value)
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *FormLink) WithRegexp(name, exp string) *FormLink {
|
||||
l.regexes[name] = regexp.MustCompile(exp)
|
||||
|
||||
return l
|
||||
}
|
||||
177
platform/tester/servermock/link_headers.go
Normal file
177
platform/tester/servermock/link_headers.go
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
package servermock
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"slices"
|
||||
)
|
||||
|
||||
const (
|
||||
authorizationHeader = "Authorization"
|
||||
contentTypeHeader = "Content-Type"
|
||||
acceptHeader = "Accept"
|
||||
)
|
||||
|
||||
const (
|
||||
applicationJSONMimeType = "application/json"
|
||||
applicationFormMimeType = "application/x-www-form-urlencoded"
|
||||
)
|
||||
|
||||
type basicAuth struct {
|
||||
username, password string
|
||||
}
|
||||
|
||||
// HeaderLink validates HTTP request headers.
|
||||
type HeaderLink struct {
|
||||
values http.Header
|
||||
regexes map[string]*regexp.Regexp
|
||||
json bool
|
||||
basicAuth *basicAuth
|
||||
statusCode int
|
||||
}
|
||||
|
||||
func CheckHeader() *HeaderLink {
|
||||
return &HeaderLink{
|
||||
values: http.Header{},
|
||||
regexes: map[string]*regexp.Regexp{},
|
||||
statusCode: http.StatusBadRequest,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *HeaderLink) Bind(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
for k, v := range l.values {
|
||||
err := checkHeader(req, k, v)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), l.statusCode)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
for k, exp := range l.regexes {
|
||||
value := req.Header.Get(k)
|
||||
|
||||
if !exp.MatchString(value) {
|
||||
msg := fmt.Sprintf("invalid %q header value, %q doesn't match to %q", k, value, exp)
|
||||
http.Error(rw, msg, l.statusCode)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if l.json && !l.checkJSONHeaders(rw, req) {
|
||||
return
|
||||
}
|
||||
|
||||
if l.basicAuth != nil && !l.checkBasicAuth(rw, req) {
|
||||
return
|
||||
}
|
||||
|
||||
next.ServeHTTP(rw, req)
|
||||
})
|
||||
}
|
||||
|
||||
func (l *HeaderLink) With(name, value string, values ...string) *HeaderLink {
|
||||
for _, v := range slices.Concat([]string{value}, values) {
|
||||
l.values.Add(name, v)
|
||||
}
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *HeaderLink) WithRegexp(name, exp string) *HeaderLink {
|
||||
l.regexes[name] = regexp.MustCompile(exp)
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *HeaderLink) WithJSONHeaders() *HeaderLink {
|
||||
l.json = true
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *HeaderLink) WithContentTypeFromURLEncoded() *HeaderLink {
|
||||
l.values.Set(contentTypeHeader, applicationFormMimeType)
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *HeaderLink) WithContentType(value string) *HeaderLink {
|
||||
l.values.Set(contentTypeHeader, value)
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *HeaderLink) WithAccept(value string) *HeaderLink {
|
||||
l.values.Set(acceptHeader, value)
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *HeaderLink) WithAuthorization(value string) *HeaderLink {
|
||||
l.values.Set(authorizationHeader, value)
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *HeaderLink) WithStatusCode(status int) *HeaderLink {
|
||||
if l.statusCode >= http.StatusContinue {
|
||||
l.statusCode = status
|
||||
}
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *HeaderLink) WithBasicAuth(username, password string) *HeaderLink {
|
||||
l.basicAuth = &basicAuth{username: username, password: password}
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *HeaderLink) checkBasicAuth(rw http.ResponseWriter, req *http.Request) bool {
|
||||
usr, pwd, ok := req.BasicAuth()
|
||||
if !ok {
|
||||
http.Error(rw, "missing Basic auth", l.statusCode)
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
if usr != l.basicAuth.username || pwd != l.basicAuth.password {
|
||||
msg := fmt.Sprintf("invalid credentials: got [username: %q, password: %q], want [username: %q, password: %q]",
|
||||
usr, pwd, l.basicAuth.username, l.basicAuth.password)
|
||||
http.Error(rw, msg, l.statusCode)
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (l *HeaderLink) checkJSONHeaders(rw http.ResponseWriter, req *http.Request) bool {
|
||||
err := checkHeader(req, acceptHeader, []string{applicationJSONMimeType})
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), l.statusCode)
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
if req.ContentLength > 0 {
|
||||
err = checkHeader(req, contentTypeHeader, []string{applicationJSONMimeType})
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), l.statusCode)
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func checkHeader(req *http.Request, k string, v []string) error {
|
||||
if !slices.Equal(req.Header[k], v) {
|
||||
return fmt.Errorf("invalid %q header value, got %q, want %q", k, req.Header[k], v)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
97
platform/tester/servermock/link_query.go
Normal file
97
platform/tester/servermock/link_query.go
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
package servermock
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
// QueryParameterLink validates query parameters in HTTP requests.
|
||||
// The strict flag enforces exact matches with specified query parameters.
|
||||
type QueryParameterLink struct {
|
||||
values map[string]string
|
||||
regexes map[string]*regexp.Regexp
|
||||
strict bool
|
||||
statusCode int
|
||||
}
|
||||
|
||||
func CheckQueryParameter() *QueryParameterLink {
|
||||
return &QueryParameterLink{
|
||||
values: map[string]string{},
|
||||
regexes: map[string]*regexp.Regexp{},
|
||||
statusCode: http.StatusBadRequest,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *QueryParameterLink) Bind(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
query := req.URL.Query()
|
||||
|
||||
if l.strict {
|
||||
if len(query) != len(l.values)+len(l.regexes) {
|
||||
msg := fmt.Sprintf("invalid query parameters, got %v, want %v", query, l.values)
|
||||
http.Error(rw, msg, l.statusCode)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
for k, v := range l.values {
|
||||
p := query.Get(k)
|
||||
if p != v {
|
||||
msg := fmt.Sprintf("invalid %q query parameter value, got %q, want %q", k, p, v)
|
||||
http.Error(rw, msg, l.statusCode)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
for k, exp := range l.regexes {
|
||||
value := query.Get(k)
|
||||
if !exp.MatchString(value) {
|
||||
msg := fmt.Sprintf("invalid %q query parameter value, %q doesn't match to %q", k, value, exp)
|
||||
http.Error(rw, msg, l.statusCode)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
next.ServeHTTP(rw, req)
|
||||
})
|
||||
}
|
||||
|
||||
func (l *QueryParameterLink) Strict() *QueryParameterLink {
|
||||
l.strict = true
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *QueryParameterLink) With(name, value string) *QueryParameterLink {
|
||||
l.values[name] = value
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *QueryParameterLink) WithRegexp(name, exp string) *QueryParameterLink {
|
||||
l.regexes[name] = regexp.MustCompile(exp)
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *QueryParameterLink) WithValues(values url.Values) *QueryParameterLink {
|
||||
for k, v := range values {
|
||||
if len(v) != 1 {
|
||||
continue
|
||||
}
|
||||
|
||||
l.values[k] = v[0]
|
||||
}
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *QueryParameterLink) WithStatusCode(status int) *QueryParameterLink {
|
||||
if l.statusCode >= http.StatusContinue {
|
||||
l.statusCode = status
|
||||
}
|
||||
|
||||
return l
|
||||
}
|
||||
89
platform/tester/servermock/link_request_body.go
Normal file
89
platform/tester/servermock/link_request_body.go
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
package servermock
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
)
|
||||
|
||||
// RequestBodyLink represents a handler utility to validate HTTP request bodies against a predefined byte slice.
|
||||
type RequestBodyLink struct {
|
||||
body []byte
|
||||
filename string
|
||||
ignoreWhitespace bool
|
||||
}
|
||||
|
||||
// CheckRequestBody creates a [RequestBodyLink] initialized with the provided request body string.
|
||||
func CheckRequestBody(body string) *RequestBodyLink {
|
||||
return &RequestBodyLink{body: []byte(body)}
|
||||
}
|
||||
|
||||
// CheckRequestBodyFromFile creates a [RequestBodyLink] initialized with the provided request body file.
|
||||
func CheckRequestBodyFromFile(filename string) *RequestBodyLink {
|
||||
return &RequestBodyLink{filename: filename}
|
||||
}
|
||||
|
||||
func (l *RequestBodyLink) Bind(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
if req.ContentLength == 0 {
|
||||
http.Error(rw, fmt.Sprintf("%s: empty request body", req.URL.Path), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
_ = req.Body.Close()
|
||||
|
||||
expectedRaw := slices.Clone(l.body)
|
||||
|
||||
if l.filename != "" {
|
||||
expectedRaw, err = os.ReadFile(filepath.Join("fixtures", l.filename))
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if len(expectedRaw) == 0 {
|
||||
http.Error(rw, fmt.Sprintf("%s: empty expected request body", req.URL.Path), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if l.ignoreWhitespace {
|
||||
body = trimLineSpace(body)
|
||||
expectedRaw = trimLineSpace(expectedRaw)
|
||||
}
|
||||
|
||||
if !bytes.Equal(bytes.TrimSpace(expectedRaw), bytes.TrimSpace(body)) {
|
||||
msg := fmt.Sprintf("%s: request body differences: got: %s, want: %s", req.URL.Path,
|
||||
string(bytes.TrimSpace(body)), string(bytes.TrimSpace(expectedRaw)))
|
||||
http.Error(rw, msg, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
next.ServeHTTP(rw, req)
|
||||
})
|
||||
}
|
||||
|
||||
func (l *RequestBodyLink) IgnoreWhitespace() *RequestBodyLink {
|
||||
l.ignoreWhitespace = true
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
func trimLineSpace(body []byte) []byte {
|
||||
buf := bytes.NewBuffer(nil)
|
||||
for line := range bytes.Lines(body) {
|
||||
buf.Write(bytes.TrimSpace(line))
|
||||
}
|
||||
|
||||
return buf.Bytes()
|
||||
}
|
||||
99
platform/tester/servermock/link_request_body_json.go
Normal file
99
platform/tester/servermock/link_request_body_json.go
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
package servermock
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
)
|
||||
|
||||
// RequestBodyJSONLink validates JSON request bodies.
|
||||
type RequestBodyJSONLink struct {
|
||||
body []byte
|
||||
filename string
|
||||
data any
|
||||
}
|
||||
|
||||
// CheckRequestJSONBody creates a [RequestBodyJSONLink] initialized with a string.
|
||||
func CheckRequestJSONBody(body string) *RequestBodyJSONLink {
|
||||
return &RequestBodyJSONLink{body: []byte(body)}
|
||||
}
|
||||
|
||||
// CheckRequestJSONBodyFromStruct creates a [RequestBodyJSONLink] initialized with a struct.
|
||||
func CheckRequestJSONBodyFromStruct(data any) *RequestBodyJSONLink {
|
||||
return &RequestBodyJSONLink{data: data}
|
||||
}
|
||||
|
||||
// CheckRequestJSONBodyFromFile creates a [RequestBodyJSONLink] initialized with the provided request body file.
|
||||
func CheckRequestJSONBodyFromFile(filename string) *RequestBodyJSONLink {
|
||||
return &RequestBodyJSONLink{filename: filename}
|
||||
}
|
||||
|
||||
func (l *RequestBodyJSONLink) Bind(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
if req.ContentLength == 0 {
|
||||
http.Error(rw, fmt.Sprintf("%s: empty request body", req.URL.Path), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
_ = req.Body.Close()
|
||||
|
||||
var expected, actual any
|
||||
|
||||
expectedRaw := slices.Clone(l.body)
|
||||
|
||||
switch {
|
||||
case l.filename != "":
|
||||
expectedRaw, err = os.ReadFile(filepath.Join("fixtures", l.filename))
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
case l.data != nil:
|
||||
expectedRaw, err = json.Marshal(l.data)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if len(expectedRaw) == 0 {
|
||||
http.Error(rw, fmt.Sprintf("%s: empty expected request body", req.URL.Path), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
err = json.Unmarshal(expectedRaw, &expected)
|
||||
if err != nil {
|
||||
msg := fmt.Sprintf("%s: the expected request body is not valid JSON: %v", req.URL.Path, err)
|
||||
http.Error(rw, msg, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
err = json.Unmarshal(body, &actual)
|
||||
if err != nil {
|
||||
msg := fmt.Sprintf("%s: request body is not valid JSON: %v", req.URL.Path, err)
|
||||
http.Error(rw, msg, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if !cmp.Equal(actual, expected) {
|
||||
msg := fmt.Sprintf("%s: request body differences: %s", req.URL.Path, cmp.Diff(actual, expected))
|
||||
http.Error(rw, msg, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
next.ServeHTTP(rw, req)
|
||||
})
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ import (
|
|||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/go-acme/lego/v4/platform/tester/servermock"
|
||||
"github.com/nrdcg/goacmedns"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
|
@ -166,11 +167,17 @@ func TestPresent_httpStorage(t *testing.T) {
|
|||
|
||||
for _, test := range testCases {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
mux := http.NewServeMux()
|
||||
server := httptest.NewServer(mux)
|
||||
config := servermock.NewBuilder(func(server *httptest.Server) (*Config, error) {
|
||||
cfg := NewDefaultConfig()
|
||||
cfg.StorageBaseURL = server.URL
|
||||
|
||||
config := NewDefaultConfig()
|
||||
config.StorageBaseURL = server.URL
|
||||
return cfg, nil
|
||||
}).
|
||||
// Fetch
|
||||
Route("GET /example.com", servermock.Noop().WithStatusCode(http.StatusNotFound)).
|
||||
// Put
|
||||
Route("POST /example.com", servermock.Noop().WithStatusCode(test.StatusCode)).
|
||||
Build(t)
|
||||
|
||||
p, err := NewDNSProviderConfig(config)
|
||||
require.NoError(t, err)
|
||||
|
|
@ -178,16 +185,6 @@ func TestPresent_httpStorage(t *testing.T) {
|
|||
client := newMockClient().WithRegisterAccount(egTestAccount)
|
||||
p.client = client
|
||||
|
||||
// Fetch
|
||||
mux.HandleFunc("GET /example.com", func(rw http.ResponseWriter, reg *http.Request) {
|
||||
rw.WriteHeader(http.StatusNotFound)
|
||||
})
|
||||
|
||||
// Put
|
||||
mux.HandleFunc("POST /example.com", func(rw http.ResponseWriter, req *http.Request) {
|
||||
rw.WriteHeader(test.StatusCode)
|
||||
})
|
||||
|
||||
err = p.Present(egDomain, "foo", egKeyAuth)
|
||||
if test.ExpectedError != nil {
|
||||
assert.Equal(t, test.ExpectedError, err)
|
||||
|
|
@ -225,22 +222,21 @@ func TestRegister_httpStorage(t *testing.T) {
|
|||
|
||||
for _, test := range testCases {
|
||||
t.Run(test.Name, func(t *testing.T) {
|
||||
mux := http.NewServeMux()
|
||||
server := httptest.NewServer(mux)
|
||||
config := servermock.NewBuilder(func(server *httptest.Server) (*Config, error) {
|
||||
cfg := NewDefaultConfig()
|
||||
cfg.StorageBaseURL = server.URL
|
||||
|
||||
config := NewDefaultConfig()
|
||||
config.StorageBaseURL = server.URL
|
||||
return cfg, nil
|
||||
}).
|
||||
// Put
|
||||
Route("POST /example.com", servermock.Noop().WithStatusCode(test.StatusCode)).
|
||||
Build(t)
|
||||
|
||||
p, err := NewDNSProviderConfig(config)
|
||||
require.NoError(t, err)
|
||||
|
||||
p.client = newMockClient().WithRegisterAccount(egTestAccount)
|
||||
|
||||
// Put
|
||||
mux.HandleFunc("POST /example.com", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(test.StatusCode)
|
||||
})
|
||||
|
||||
acc, err := p.register(t.Context(), egDomain, egFQDN)
|
||||
if test.ExpectedError != nil {
|
||||
assert.Equal(t, test.ExpectedError, err)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"fulldomain": "foo.example.com",
|
||||
"subdomain": "foo",
|
||||
"username": "user",
|
||||
"password": "secret",
|
||||
"server_url": "https://example.com"
|
||||
}
|
||||
|
|
@ -1,57 +1,35 @@
|
|||
package internal
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/go-acme/lego/v4/platform/tester/servermock"
|
||||
"github.com/nrdcg/goacmedns"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func setupTest(t *testing.T, pattern, filename string, statusCode int) *HTTPStorage {
|
||||
t.Helper()
|
||||
func mockBuilder() *servermock.Builder[*HTTPStorage] {
|
||||
return servermock.NewBuilder[*HTTPStorage](
|
||||
func(server *httptest.Server) (*HTTPStorage, error) {
|
||||
storage, err := NewHTTPStorage(server.URL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mux := http.NewServeMux()
|
||||
server := httptest.NewServer(mux)
|
||||
t.Cleanup(server.Close)
|
||||
storage.client = server.Client()
|
||||
|
||||
mux.HandleFunc(pattern, func(rw http.ResponseWriter, req *http.Request) {
|
||||
if filename == "" {
|
||||
rw.WriteHeader(statusCode)
|
||||
return
|
||||
}
|
||||
|
||||
file, err := os.Open(filepath.Join("fixtures", filename))
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
defer func() { _ = file.Close() }()
|
||||
|
||||
rw.WriteHeader(statusCode)
|
||||
_, err = io.Copy(rw, file)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
storage, err := NewHTTPStorage(server.URL)
|
||||
require.NoError(t, err)
|
||||
|
||||
storage.client = server.Client()
|
||||
|
||||
return storage
|
||||
return storage, nil
|
||||
},
|
||||
servermock.CheckHeader().WithJSONHeaders())
|
||||
}
|
||||
|
||||
func TestHTTPStorage_Fetch(t *testing.T) {
|
||||
storage := setupTest(t, "GET /example.com", "fetch.json", http.StatusOK)
|
||||
storage := mockBuilder().
|
||||
Route("GET /example.com", servermock.ResponseFromFixture("fetch.json")).
|
||||
Build(t)
|
||||
|
||||
account, err := storage.Fetch(t.Context(), "example.com")
|
||||
require.NoError(t, err)
|
||||
|
|
@ -68,14 +46,20 @@ func TestHTTPStorage_Fetch(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestHTTPStorage_Fetch_error(t *testing.T) {
|
||||
storage := setupTest(t, "GET /example.com", "error.json", http.StatusInternalServerError)
|
||||
storage := mockBuilder().
|
||||
Route("GET /example.com",
|
||||
servermock.ResponseFromFixture("error.json").
|
||||
WithStatusCode(http.StatusInternalServerError)).
|
||||
Build(t)
|
||||
|
||||
_, err := storage.Fetch(t.Context(), "example.com")
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestHTTPStorage_FetchAll(t *testing.T) {
|
||||
storage := setupTest(t, "GET /", "fetch-all.json", http.StatusOK)
|
||||
storage := mockBuilder().
|
||||
Route("GET /", servermock.ResponseFromFixture("fetch-all.json")).
|
||||
Build(t)
|
||||
|
||||
account, err := storage.FetchAll(t.Context())
|
||||
require.NoError(t, err)
|
||||
|
|
@ -101,14 +85,21 @@ func TestHTTPStorage_FetchAll(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestHTTPStorage_FetchAll_error(t *testing.T) {
|
||||
storage := setupTest(t, "GET /", "error.json", http.StatusInternalServerError)
|
||||
storage := mockBuilder().
|
||||
Route("GET /",
|
||||
servermock.ResponseFromFixture("error.json").
|
||||
WithStatusCode(http.StatusInternalServerError)).
|
||||
Build(t)
|
||||
|
||||
_, err := storage.FetchAll(t.Context())
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestHTTPStorage_Put(t *testing.T) {
|
||||
storage := setupTest(t, "POST /example.com", "", http.StatusOK)
|
||||
storage := mockBuilder().
|
||||
Route("POST /example.com", nil,
|
||||
servermock.CheckRequestJSONBodyFromFile("request-body.json")).
|
||||
Build(t)
|
||||
|
||||
account := goacmedns.Account{
|
||||
FullDomain: "foo.example.com",
|
||||
|
|
@ -123,7 +114,11 @@ func TestHTTPStorage_Put(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestHTTPStorage_Put_error(t *testing.T) {
|
||||
storage := setupTest(t, "POST /example.com", "error.json", http.StatusInternalServerError)
|
||||
storage := mockBuilder().
|
||||
Route("POST /example.com",
|
||||
servermock.ResponseFromFixture("error.json").
|
||||
WithStatusCode(http.StatusInternalServerError)).
|
||||
Build(t)
|
||||
|
||||
account := goacmedns.Account{
|
||||
FullDomain: "foo.example.com",
|
||||
|
|
@ -138,7 +133,12 @@ func TestHTTPStorage_Put_error(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestHTTPStorage_Put_CNAME_created(t *testing.T) {
|
||||
storage := setupTest(t, "POST /example.com", "", http.StatusCreated)
|
||||
storage := mockBuilder().
|
||||
Route("POST /example.com",
|
||||
servermock.Noop().
|
||||
WithStatusCode(http.StatusCreated),
|
||||
servermock.CheckRequestJSONBodyFromFile("request-body.json")).
|
||||
Build(t)
|
||||
|
||||
account := goacmedns.Account{
|
||||
FullDomain: "foo.example.com",
|
||||
|
|
|
|||
|
|
@ -1,27 +1,28 @@
|
|||
package internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/go-acme/lego/v4/platform/tester/servermock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestClient_GetDNSSettings(t *testing.T) {
|
||||
mux := http.NewServeMux()
|
||||
server := httptest.NewServer(mux)
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
mux.HandleFunc("/", testHandler("get_dns_settings.xml"))
|
||||
|
||||
func setupClient(server *httptest.Server) (*Client, error) {
|
||||
client := NewClient("user")
|
||||
client.baseURL = server.URL
|
||||
client.HTTPClient = server.Client()
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func TestClient_GetDNSSettings(t *testing.T) {
|
||||
client := servermock.NewBuilder[*Client](setupClient).
|
||||
Route("POST /", servermock.ResponseFromFixture("get_dns_settings.xml"),
|
||||
servermock.CheckRequestBodyFromFile("get_dns_settings-request.xml").
|
||||
IgnoreWhitespace()).
|
||||
Build(t)
|
||||
|
||||
records, err := client.GetDNSSettings(mockContext(t), "example.com", "")
|
||||
require.NoError(t, err)
|
||||
|
|
@ -96,14 +97,11 @@ func TestClient_GetDNSSettings(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_AddDNSSettings(t *testing.T) {
|
||||
mux := http.NewServeMux()
|
||||
server := httptest.NewServer(mux)
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
mux.HandleFunc("/", testHandler("add_dns_settings.xml"))
|
||||
|
||||
client := NewClient("user")
|
||||
client.baseURL = server.URL
|
||||
client := servermock.NewBuilder[*Client](setupClient).
|
||||
Route("POST /", servermock.ResponseFromFixture("add_dns_settings.xml"),
|
||||
servermock.CheckRequestBodyFromFile("add_dns_settings-request.xml").
|
||||
IgnoreWhitespace()).
|
||||
Build(t)
|
||||
|
||||
record := DNSRequest{
|
||||
ZoneHost: "42cnc.de.",
|
||||
|
|
@ -119,40 +117,14 @@ func TestClient_AddDNSSettings(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_DeleteDNSSettings(t *testing.T) {
|
||||
mux := http.NewServeMux()
|
||||
server := httptest.NewServer(mux)
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
mux.HandleFunc("/", testHandler("delete_dns_settings.xml"))
|
||||
|
||||
client := NewClient("user")
|
||||
client.baseURL = server.URL
|
||||
client := servermock.NewBuilder[*Client](setupClient).
|
||||
Route("POST /", servermock.ResponseFromFixture("delete_dns_settings.xml"),
|
||||
servermock.CheckRequestBodyFromFile("delete_dns_settings-request.xml").
|
||||
IgnoreWhitespace()).
|
||||
Build(t)
|
||||
|
||||
r, err := client.DeleteDNSSettings(mockContext(t), "57347450")
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, "TRUE", r)
|
||||
}
|
||||
|
||||
func testHandler(filename string) http.HandlerFunc {
|
||||
return func(rw http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != http.MethodPost {
|
||||
http.Error(rw, fmt.Sprintf("unsupported method: %s", req.Method), http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
file, err := os.Open(filepath.Join("fixtures", filename))
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
defer func() { _ = file.Close() }()
|
||||
|
||||
_, err = io.Copy(rw, file)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/">
|
||||
<Body>
|
||||
<KasApi xmlns="https://kasserver.com/">
|
||||
<Params>{"kas_login":"user","kas_auth_type":"session","kas_auth_data":"593959ca04f0de9689b586c6a647d15d","kas_action":"add_dns_settings","KasRequestParams":{"zone_host":"42cnc.de.","record_type":"TXT","record_name":"lego","record_data":"abcdefgh","record_aux":0}}</Params>
|
||||
</KasApi>
|
||||
</Body>
|
||||
</Envelope>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/">
|
||||
<Body>
|
||||
<KasApi xmlns="https://kasserver.com/">
|
||||
<Params>{"kas_login":"user","kas_auth_type":"session","kas_auth_data":"593959ca04f0de9689b586c6a647d15d","kas_action":"delete_dns_settings","KasRequestParams":{"record_id":"57347450"}}</Params>
|
||||
</KasApi>
|
||||
</Body>
|
||||
</Envelope>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/">
|
||||
<Body>
|
||||
<KasApi xmlns="https://kasserver.com/">
|
||||
<Params>{"kas_login":"user","kas_auth_type":"session","kas_auth_data":"593959ca04f0de9689b586c6a647d15d","kas_action":"get_dns_settings","KasRequestParams":{"zone_host":"example.com"}}</Params>
|
||||
</KasApi>
|
||||
</Body>
|
||||
</Envelope>
|
||||
|
|
@ -2,14 +2,21 @@ package internal
|
|||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/go-acme/lego/v4/platform/tester/servermock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func setupIdentifierClient(server *httptest.Server) (*Identifier, error) {
|
||||
client := NewIdentifier("user", "secret")
|
||||
client.authEndpoint = server.URL
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func mockContext(t *testing.T) context.Context {
|
||||
t.Helper()
|
||||
|
||||
|
|
@ -17,14 +24,9 @@ func mockContext(t *testing.T) context.Context {
|
|||
}
|
||||
|
||||
func TestIdentifier_Authentication(t *testing.T) {
|
||||
mux := http.NewServeMux()
|
||||
server := httptest.NewServer(mux)
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
mux.HandleFunc("/", testHandler("auth.xml"))
|
||||
|
||||
client := NewIdentifier("user", "secret")
|
||||
client.authEndpoint = server.URL
|
||||
client := servermock.NewBuilder[*Identifier](setupIdentifierClient).
|
||||
Route("POST /", servermock.ResponseFromFixture("auth.xml")).
|
||||
Build(t)
|
||||
|
||||
credentialToken, err := client.Authentication(t.Context(), 60, false)
|
||||
require.NoError(t, err)
|
||||
|
|
@ -33,14 +35,9 @@ func TestIdentifier_Authentication(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestIdentifier_Authentication_error(t *testing.T) {
|
||||
mux := http.NewServeMux()
|
||||
server := httptest.NewServer(mux)
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
mux.HandleFunc("/", testHandler("auth_fault.xml"))
|
||||
|
||||
client := NewIdentifier("user", "secret")
|
||||
client.authEndpoint = server.URL
|
||||
client := servermock.NewBuilder[*Identifier](setupIdentifierClient).
|
||||
Route("POST /", servermock.ResponseFromFixture("auth_fault.xml")).
|
||||
Build(t)
|
||||
|
||||
_, err := client.Authentication(t.Context(), 60, false)
|
||||
require.Error(t, err)
|
||||
|
|
|
|||
|
|
@ -1,64 +1,39 @@
|
|||
package internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/go-acme/lego/v4/platform/tester/servermock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func setupTest(t *testing.T, apiKey string) (*Client, *http.ServeMux) {
|
||||
t.Helper()
|
||||
func mockBuilder(apiKey string) *servermock.Builder[*Client] {
|
||||
return servermock.NewBuilder[*Client](
|
||||
func(server *httptest.Server) (*Client, error) {
|
||||
client := NewClient(apiKey)
|
||||
client.baseURL, _ = url.Parse(server.URL)
|
||||
client.HTTPClient = server.Client()
|
||||
|
||||
mux := http.NewServeMux()
|
||||
server := httptest.NewServer(mux)
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
client := NewClient(apiKey)
|
||||
client.baseURL, _ = url.Parse(server.URL)
|
||||
client.HTTPClient = server.Client()
|
||||
|
||||
return client, mux
|
||||
return client, nil
|
||||
},
|
||||
servermock.CheckHeader().WithJSONHeaders().
|
||||
WithAuthorization(apiKey))
|
||||
}
|
||||
|
||||
func TestClient_GetTxtRecord(t *testing.T) {
|
||||
const apiKey = "myKeyA"
|
||||
|
||||
client, mux := setupTest(t, apiKey)
|
||||
|
||||
const domain = "example.com"
|
||||
|
||||
mux.HandleFunc("/cdn/4.0/domains/"+domain+"/dns-records", func(rw http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != http.MethodGet {
|
||||
http.Error(rw, fmt.Sprintf("unsupported method: %s", req.Method), http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
auth := req.Header.Get(authorizationHeader)
|
||||
if auth != apiKey {
|
||||
http.Error(rw, fmt.Sprintf("invalid API key: %s", auth), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
file, err := os.Open("./fixtures/get_txt_record.json")
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer func() { _ = file.Close() }()
|
||||
|
||||
_, err = io.Copy(rw, file)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
})
|
||||
client := mockBuilder(apiKey).
|
||||
Route("GET /cdn/4.0/domains/"+domain+"/dns-records",
|
||||
servermock.ResponseFromFixture("get_txt_record.json"),
|
||||
servermock.CheckQueryParameter().With("search", "acme-challenge")).
|
||||
Build(t)
|
||||
|
||||
_, err := client.GetTxtRecord(t.Context(), domain, "_acme-challenge", "txtxtxt")
|
||||
require.NoError(t, err)
|
||||
|
|
@ -67,36 +42,14 @@ func TestClient_GetTxtRecord(t *testing.T) {
|
|||
func TestClient_CreateRecord(t *testing.T) {
|
||||
const apiKey = "myKeyB"
|
||||
|
||||
client, mux := setupTest(t, apiKey)
|
||||
|
||||
const domain = "example.com"
|
||||
|
||||
mux.HandleFunc("/cdn/4.0/domains/"+domain+"/dns-records", func(rw http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != http.MethodPost {
|
||||
http.Error(rw, fmt.Sprintf("unsupported method: %s", req.Method), http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
auth := req.Header.Get(authorizationHeader)
|
||||
if auth != apiKey {
|
||||
http.Error(rw, fmt.Sprintf("invalid API key: %s", auth), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
file, err := os.Open("./fixtures/create_txt_record.json")
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer func() { _ = file.Close() }()
|
||||
|
||||
rw.WriteHeader(http.StatusCreated)
|
||||
_, err = io.Copy(rw, file)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
})
|
||||
client := mockBuilder(apiKey).
|
||||
Route("POST /cdn/4.0/domains/"+domain+"/dns-records",
|
||||
servermock.ResponseFromFixture("create_txt_record.json").
|
||||
WithStatusCode(http.StatusCreated),
|
||||
servermock.CheckRequestJSONBodyFromFile("create_record-request.json")).
|
||||
Build(t)
|
||||
|
||||
record := DNSRecord{
|
||||
Name: "_acme-challenge",
|
||||
|
|
@ -128,23 +81,12 @@ func TestClient_CreateRecord(t *testing.T) {
|
|||
func TestClient_DeleteRecord(t *testing.T) {
|
||||
const apiKey = "myKeyC"
|
||||
|
||||
client, mux := setupTest(t, apiKey)
|
||||
|
||||
const domain = "example.com"
|
||||
const recordID = "recordId"
|
||||
|
||||
mux.HandleFunc("/cdn/4.0/domains/"+domain+"/dns-records/"+recordID, func(rw http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != http.MethodDelete {
|
||||
http.Error(rw, fmt.Sprintf("unsupported method: %s", req.Method), http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
auth := req.Header.Get(authorizationHeader)
|
||||
if auth != apiKey {
|
||||
http.Error(rw, fmt.Sprintf("invalid API key: %s", auth), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
})
|
||||
client := mockBuilder(apiKey).
|
||||
Route("DELETE /cdn/4.0/domains/"+domain+"/dns-records/"+recordID, nil).
|
||||
Build(t)
|
||||
|
||||
err := client.DeleteRecord(t.Context(), domain, recordID)
|
||||
require.NoError(t, err)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"type": "txt",
|
||||
"value": {
|
||||
"text": "txtxtxt"
|
||||
},
|
||||
"name": "_acme-challenge",
|
||||
"ttl": 600
|
||||
}
|
||||
|
|
@ -1,35 +1,32 @@
|
|||
package auroradns
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/go-acme/lego/v4/platform/tester"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/go-acme/lego/v4/platform/tester/servermock"
|
||||
"github.com/nrdcg/auroradns"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var envTest = tester.NewEnvTest(EnvAPIKey, EnvSecret)
|
||||
|
||||
func setupTest(t *testing.T) (*DNSProvider, *http.ServeMux) {
|
||||
t.Helper()
|
||||
func mockBuilder() *servermock.Builder[*DNSProvider] {
|
||||
return servermock.NewBuilder(
|
||||
func(server *httptest.Server) (*DNSProvider, error) {
|
||||
config := NewDefaultConfig()
|
||||
config.APIKey = "asdf1234"
|
||||
config.Secret = "key"
|
||||
config.BaseURL = server.URL
|
||||
|
||||
mux := http.NewServeMux()
|
||||
server := httptest.NewServer(mux)
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
config := NewDefaultConfig()
|
||||
config.APIKey = "asdf1234"
|
||||
config.Secret = "key"
|
||||
config.BaseURL = server.URL
|
||||
|
||||
provider, err := NewDNSProviderConfig(config)
|
||||
require.NoError(t, err)
|
||||
|
||||
return provider, mux
|
||||
return NewDNSProviderConfig(config)
|
||||
},
|
||||
servermock.CheckHeader().
|
||||
WithContentType("application/json").
|
||||
WithRegexp("Authorization", `AuroraDNSv1 .+`).
|
||||
WithRegexp("X-Auroradns-Date", `[0-9TZ]+`))
|
||||
}
|
||||
|
||||
func TestNewDNSProvider(t *testing.T) {
|
||||
|
|
@ -145,72 +142,47 @@ func TestNewDNSProviderConfig(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestDNSProvider_Present(t *testing.T) {
|
||||
provider, mux := setupTest(t)
|
||||
|
||||
mux.HandleFunc("/zones", func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Equal(t, http.MethodGet, r.Method, "method")
|
||||
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
fmt.Fprintf(w, `[{
|
||||
"id": "c56a4180-65aa-42ec-a945-5fd21dec0538",
|
||||
"name": "example.com"
|
||||
}]`)
|
||||
})
|
||||
|
||||
mux.HandleFunc("/zones/c56a4180-65aa-42ec-a945-5fd21dec0538/records", func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Equal(t, http.MethodPost, r.Method)
|
||||
assert.Equal(t, "application/json", r.Header.Get("Content-Type"), "Content-Type")
|
||||
|
||||
reqBody, err := io.ReadAll(r.Body)
|
||||
require.NoError(t, err)
|
||||
assert.JSONEq(t, `{"type":"TXT","name":"_acme-challenge","content":"w6uP8Tcg6K2QR905Rms8iXTlksL6OD1KOWBxTK7wxPI","ttl":300}`, string(reqBody))
|
||||
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
fmt.Fprintf(w, `{
|
||||
"id": "c56a4180-65aa-42ec-a945-5fd21dec0538",
|
||||
"type": "TXT",
|
||||
"name": "_acme-challenge",
|
||||
"ttl": 300
|
||||
}`)
|
||||
})
|
||||
provider := mockBuilder().
|
||||
Route("GET /zones",
|
||||
servermock.JSONEncode([]auroradns.Zone{{
|
||||
ID: "c56a4180-65aa-42ec-a945-5fd21dec0538",
|
||||
Name: "example.com",
|
||||
}}).
|
||||
WithStatusCode(http.StatusCreated)).
|
||||
Route("POST /zones/c56a4180-65aa-42ec-a945-5fd21dec0538/records",
|
||||
servermock.JSONEncode(auroradns.Record{
|
||||
ID: "ec56a4180-65aa-42ec-a945-5fd21dec0538",
|
||||
RecordType: "TXT",
|
||||
Name: "_acme-challenge",
|
||||
TTL: 300,
|
||||
}).
|
||||
WithStatusCode(http.StatusCreated)).
|
||||
Build(t)
|
||||
|
||||
err := provider.Present("example.com", "", "foobar")
|
||||
require.NoError(t, err, "fail to create TXT record")
|
||||
}
|
||||
|
||||
func TestDNSProvider_CleanUp(t *testing.T) {
|
||||
provider, mux := setupTest(t)
|
||||
|
||||
mux.HandleFunc("/zones", func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Equal(t, http.MethodGet, r.Method)
|
||||
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
fmt.Fprintf(w, `[{
|
||||
"id": "c56a4180-65aa-42ec-a945-5fd21dec0538",
|
||||
"name": "example.com"
|
||||
}]`)
|
||||
})
|
||||
|
||||
mux.HandleFunc("/zones/c56a4180-65aa-42ec-a945-5fd21dec0538/records", func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Equal(t, http.MethodPost, r.Method)
|
||||
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
fmt.Fprintf(w, `{
|
||||
"id": "ec56a4180-65aa-42ec-a945-5fd21dec0538",
|
||||
"type": "TXT",
|
||||
"name": "_acme-challenge",
|
||||
"ttl": 300
|
||||
}`)
|
||||
})
|
||||
|
||||
mux.HandleFunc("/zones/c56a4180-65aa-42ec-a945-5fd21dec0538/records/ec56a4180-65aa-42ec-a945-5fd21dec0538", func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Equal(t, http.MethodDelete, r.Method)
|
||||
|
||||
assert.Equal(t, "application/json", r.Header.Get("Content-Type"), "Content-Type")
|
||||
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
fmt.Fprintf(w, `{}`)
|
||||
})
|
||||
provider := mockBuilder().
|
||||
Route("GET /zones",
|
||||
servermock.JSONEncode([]auroradns.Zone{{
|
||||
ID: "c56a4180-65aa-42ec-a945-5fd21dec0538",
|
||||
Name: "example.com",
|
||||
}}).
|
||||
WithStatusCode(http.StatusCreated)).
|
||||
Route("POST /zones/c56a4180-65aa-42ec-a945-5fd21dec0538/records",
|
||||
servermock.JSONEncode(auroradns.Record{
|
||||
ID: "ec56a4180-65aa-42ec-a945-5fd21dec0538",
|
||||
RecordType: "TXT",
|
||||
Name: "_acme-challenge",
|
||||
TTL: 300,
|
||||
}).
|
||||
WithStatusCode(http.StatusCreated)).
|
||||
Route("DELETE /zones/c56a4180-65aa-42ec-a945-5fd21dec0538/records/ec56a4180-65aa-42ec-a945-5fd21dec0538",
|
||||
servermock.RawStringResponse("{}").
|
||||
WithStatusCode(http.StatusCreated)).
|
||||
Build(t)
|
||||
|
||||
err := provider.Present("example.com", "", "foobar")
|
||||
require.NoError(t, err, "fail to create TXT record")
|
||||
|
|
|
|||
|
|
@ -1,68 +1,37 @@
|
|||
package internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/go-acme/lego/v4/platform/tester/servermock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func setupTest(t *testing.T, method, pattern string, status int, file string) *Client {
|
||||
t.Helper()
|
||||
func mockBuilder() *servermock.Builder[*Client] {
|
||||
return servermock.NewBuilder[*Client](
|
||||
func(server *httptest.Server) (*Client, error) {
|
||||
client := NewClient("user", "secret", 123)
|
||||
client.HTTPClient = server.Client()
|
||||
client.BaseURL, _ = url.Parse(server.URL)
|
||||
|
||||
mux := http.NewServeMux()
|
||||
server := httptest.NewServer(mux)
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
mux.HandleFunc(pattern, func(rw http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != method {
|
||||
http.Error(rw, fmt.Sprintf("unsupported method: %s", req.Method), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
apiUser, apiKey, ok := req.BasicAuth()
|
||||
if apiUser != "user" || apiKey != "secret" || !ok {
|
||||
http.Error(rw, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
if file == "" {
|
||||
rw.WriteHeader(status)
|
||||
return
|
||||
}
|
||||
|
||||
open, err := os.Open(filepath.Join("fixtures", file))
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
defer func() { _ = open.Close() }()
|
||||
|
||||
rw.WriteHeader(status)
|
||||
_, err = io.Copy(rw, open)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
client := NewClient("user", "secret", 123)
|
||||
client.HTTPClient = server.Client()
|
||||
client.BaseURL, _ = url.Parse(server.URL)
|
||||
|
||||
return client
|
||||
return client, nil
|
||||
},
|
||||
servermock.CheckHeader().
|
||||
WithBasicAuth("user", "secret").
|
||||
WithJSONHeaders())
|
||||
}
|
||||
|
||||
func TestClient_AddTxtRecords(t *testing.T) {
|
||||
client := setupTest(t, http.MethodPost, "/zone/example.com/_stream", http.StatusOK, "add-record.json")
|
||||
client := mockBuilder().
|
||||
Route("POST /zone/example.com/_stream",
|
||||
servermock.ResponseFromFixture("add_record.json"),
|
||||
servermock.CheckRequestJSONBodyFromFile("add_record-request.json"),
|
||||
servermock.CheckHeader().
|
||||
With("X-Domainrobot-Context", "123")).
|
||||
Build(t)
|
||||
|
||||
records := []*ResourceRecord{{}}
|
||||
|
||||
|
|
@ -86,7 +55,13 @@ func TestClient_AddTxtRecords(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_RemoveTXTRecords(t *testing.T) {
|
||||
client := setupTest(t, http.MethodPost, "/zone/example.com/_stream", http.StatusOK, "add-record.json")
|
||||
client := mockBuilder().
|
||||
Route("POST /zone/example.com/_stream",
|
||||
servermock.ResponseFromFixture("remove_record.json"),
|
||||
servermock.CheckRequestJSONBodyFromFile("remove_record-request.json"),
|
||||
servermock.CheckHeader().
|
||||
With("X-Domainrobot-Context", "123")).
|
||||
Build(t)
|
||||
|
||||
records := []*ResourceRecord{{}}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"adds": [
|
||||
{
|
||||
"name": "",
|
||||
"ttl": 0,
|
||||
"type": "",
|
||||
"value": ""
|
||||
}
|
||||
],
|
||||
"rems": null
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"adds": null,
|
||||
"rems": [
|
||||
{
|
||||
"name": "",
|
||||
"ttl": 0,
|
||||
"type": "",
|
||||
"value": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,58 +1,37 @@
|
|||
package internal
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/go-acme/lego/v4/platform/tester/servermock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func setupTest(t *testing.T, pattern string, status int, filename string) *Client {
|
||||
t.Helper()
|
||||
|
||||
mux := http.NewServeMux()
|
||||
server := httptest.NewServer(mux)
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
mux.HandleFunc(pattern, func(rw http.ResponseWriter, req *http.Request) {
|
||||
if filename == "" {
|
||||
rw.WriteHeader(status)
|
||||
return
|
||||
}
|
||||
|
||||
file, err := os.Open(filepath.Join("fixtures", filename))
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
defer func() { _ = file.Close() }()
|
||||
|
||||
rw.WriteHeader(status)
|
||||
_, err = io.Copy(rw, file)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
func setupClient(server *httptest.Server) (*Client, error) {
|
||||
client, err := NewClient("user", "secret")
|
||||
require.NoError(t, err)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client.HTTPClient = server.Client()
|
||||
client.baseURL, _ = url.Parse(server.URL)
|
||||
|
||||
return client
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func TestClient_ListRecords(t *testing.T) {
|
||||
client := setupTest(t, "GET /dns_list", http.StatusOK, "dns_list.json")
|
||||
client := servermock.NewBuilder[*Client](setupClient).
|
||||
Route("GET /dns_list",
|
||||
servermock.ResponseFromFixture("dns_list.json"),
|
||||
servermock.CheckQueryParameter().Strict().
|
||||
With("domain", "example.com").
|
||||
With("nichdl", "user").
|
||||
With("token", "secret")).
|
||||
Build(t)
|
||||
|
||||
records, err := client.ListRecords(t.Context(), "example.com")
|
||||
require.NoError(t, err)
|
||||
|
|
@ -68,14 +47,26 @@ func TestClient_ListRecords(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_ListRecords_error(t *testing.T) {
|
||||
client := setupTest(t, "GET /dns_list", http.StatusNotFound, "dns_list_error.json")
|
||||
client := servermock.NewBuilder[*Client](setupClient).
|
||||
Route("GET /dns_list",
|
||||
servermock.ResponseFromFixture("dns_list_error.json").
|
||||
WithStatusCode(http.StatusNotFound)).
|
||||
Build(t)
|
||||
|
||||
_, err := client.ListRecords(t.Context(), "example.com")
|
||||
require.EqualError(t, err, "error: Domain not found (1)")
|
||||
}
|
||||
|
||||
func TestClient_DeleteRecord(t *testing.T) {
|
||||
client := setupTest(t, "GET /dns_delete", http.StatusOK, "dns_delete.json")
|
||||
client := servermock.NewBuilder[*Client](setupClient).
|
||||
Route("GET /dns_delete",
|
||||
servermock.ResponseFromFixture("dns_delete.json"),
|
||||
servermock.CheckQueryParameter().Strict().
|
||||
With("id", "74749").
|
||||
With("domain", "example.com").
|
||||
With("nichdl", "user").
|
||||
With("token", "secret")).
|
||||
Build(t)
|
||||
|
||||
record := Record{ID: "74749"}
|
||||
|
||||
|
|
@ -84,7 +75,11 @@ func TestClient_DeleteRecord(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_DeleteRecord_error(t *testing.T) {
|
||||
client := setupTest(t, "GET /dns_delete", http.StatusNotFound, "dns_delete_error.json")
|
||||
client := servermock.NewBuilder[*Client](setupClient).
|
||||
Route("GET /dns_delete",
|
||||
servermock.ResponseFromFixture("dns_delete_error.json").
|
||||
WithStatusCode(http.StatusNotFound)).
|
||||
Build(t)
|
||||
|
||||
record := Record{ID: "74749"}
|
||||
|
||||
|
|
@ -93,7 +88,15 @@ func TestClient_DeleteRecord_error(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_AddRecord(t *testing.T) {
|
||||
client := setupTest(t, "GET /dns_add", http.StatusOK, "dns_add.json")
|
||||
client := servermock.NewBuilder[*Client](setupClient).
|
||||
Route("GET /dns_add",
|
||||
servermock.ResponseFromFixture("dns_add.json"),
|
||||
servermock.CheckQueryParameter().Strict().
|
||||
With("id", "74749").
|
||||
With("domain", "example.com").
|
||||
With("nichdl", "user").
|
||||
With("token", "secret")).
|
||||
Build(t)
|
||||
|
||||
record := Record{ID: "74749"}
|
||||
|
||||
|
|
@ -102,7 +105,11 @@ func TestClient_AddRecord(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_AddRecord_error(t *testing.T) {
|
||||
client := setupTest(t, "GET /dns_add", http.StatusNotFound, "dns_add_error.json")
|
||||
client := servermock.NewBuilder[*Client](setupClient).
|
||||
Route("GET /dns_add",
|
||||
servermock.ResponseFromFixture("dns_add_error.json").
|
||||
WithStatusCode(http.StatusNotFound)).
|
||||
Build(t)
|
||||
|
||||
record := Record{ID: "74749"}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,15 +2,12 @@ package azion
|
|||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/aziontech/azionapi-go-sdk/idns"
|
||||
"github.com/go-acme/lego/v4/platform/tester"
|
||||
"github.com/go-acme/lego/v4/platform/tester/servermock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
|
@ -123,8 +120,9 @@ func TestLiveCleanUp(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestDNSProvider_findZone(t *testing.T) {
|
||||
provider, mux := setupTest(t)
|
||||
mux.HandleFunc("GET /intelligent_dns", writeFixtureHandler("zones.json"))
|
||||
provider := mockBuilder().
|
||||
Route("GET /intelligent_dns", servermock.ResponseFromFixture("zones.json")).
|
||||
Build(t)
|
||||
|
||||
testCases := []struct {
|
||||
desc string
|
||||
|
|
@ -198,8 +196,9 @@ func TestDNSProvider_findZone_error(t *testing.T) {
|
|||
|
||||
for _, test := range testCases {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
provider, mux := setupTest(t)
|
||||
mux.HandleFunc("GET /intelligent_dns", writeFixtureHandler(test.response))
|
||||
provider := mockBuilder().
|
||||
Route("GET /intelligent_dns", servermock.ResponseFromFixture(test.response)).
|
||||
Build(t)
|
||||
|
||||
zone, err := provider.findZone(context.Background(), test.fqdn)
|
||||
require.EqualError(t, err, test.expected)
|
||||
|
|
@ -209,41 +208,25 @@ func TestDNSProvider_findZone_error(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func setupTest(t *testing.T) (*DNSProvider, *http.ServeMux) {
|
||||
t.Helper()
|
||||
func mockBuilder() *servermock.Builder[*DNSProvider] {
|
||||
return servermock.NewBuilder(
|
||||
func(server *httptest.Server) (*DNSProvider, error) {
|
||||
config := NewDefaultConfig()
|
||||
config.PersonalToken = "secret"
|
||||
|
||||
mux := http.NewServeMux()
|
||||
server := httptest.NewServer(mux)
|
||||
provider, err := NewDNSProviderConfig(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
config := NewDefaultConfig()
|
||||
config.PersonalToken = "secret"
|
||||
clientConfig := provider.client.GetConfig()
|
||||
clientConfig.HTTPClient = server.Client()
|
||||
clientConfig.Servers = idns.ServerConfigurations{{
|
||||
URL: server.URL,
|
||||
Description: "Production",
|
||||
}}
|
||||
|
||||
provider, err := NewDNSProviderConfig(config)
|
||||
require.NoError(t, err)
|
||||
|
||||
clientConfig := provider.client.GetConfig()
|
||||
clientConfig.HTTPClient = server.Client()
|
||||
clientConfig.Servers = idns.ServerConfigurations{
|
||||
{
|
||||
URL: server.URL,
|
||||
Description: "Production",
|
||||
return provider, nil
|
||||
},
|
||||
}
|
||||
|
||||
return provider, mux
|
||||
}
|
||||
|
||||
func writeFixtureHandler(filename string) http.HandlerFunc {
|
||||
return func(rw http.ResponseWriter, req *http.Request) {
|
||||
rw.Header().Set("Content-Type", "application/json")
|
||||
|
||||
file, err := os.Open(filepath.Join("fixtures", filename))
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer func() { _ = file.Close() }()
|
||||
|
||||
_, _ = io.Copy(rw, file)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,33 +6,37 @@ import (
|
|||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/go-acme/lego/v4/platform/tester/servermock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestClient_LookupParentZoneID(t *testing.T) {
|
||||
mux := http.NewServeMux()
|
||||
server := httptest.NewServer(mux)
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
func setupClient(server *httptest.Server) (*Client, error) {
|
||||
client := NewClient(server.URL, "user", "secret")
|
||||
client.HTTPClient = server.Client()
|
||||
|
||||
mux.HandleFunc("/Services/REST/v1/getEntityByName", func(rw http.ResponseWriter, req *http.Request) {
|
||||
query := req.URL.Query()
|
||||
return client, nil
|
||||
}
|
||||
|
||||
if query.Get("name") == "com" {
|
||||
_ = json.NewEncoder(rw).Encode(EntityResponse{
|
||||
ID: 2,
|
||||
Name: "com",
|
||||
Type: ZoneType,
|
||||
Properties: "test",
|
||||
})
|
||||
return
|
||||
}
|
||||
func TestClient_LookupParentZoneID(t *testing.T) {
|
||||
client := servermock.NewBuilder[*Client](setupClient).
|
||||
Route("GET /Services/REST/v1/getEntityByName",
|
||||
http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
query := req.URL.Query()
|
||||
|
||||
http.Error(rw, "{}", http.StatusOK)
|
||||
})
|
||||
if query.Get("name") == "com" {
|
||||
_ = json.NewEncoder(rw).Encode(EntityResponse{
|
||||
ID: 2,
|
||||
Name: "com",
|
||||
Type: ZoneType,
|
||||
Properties: "test",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
_, _ = rw.Write([]byte(`{}`))
|
||||
})).
|
||||
Build(t)
|
||||
|
||||
parentID, name, err := client.LookupParentZoneID(t.Context(), 2, "foo.example.com")
|
||||
require.NoError(t, err)
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
package internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/go-acme/lego/v4/platform/tester/servermock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
|
@ -13,39 +11,16 @@ import (
|
|||
const fakeToken = "BAMAuthToken: dQfuRMTUxNjc3MjcyNDg1ODppcGFybXM="
|
||||
|
||||
func TestClient_CreateAuthenticatedContext(t *testing.T) {
|
||||
mux := http.NewServeMux()
|
||||
server := httptest.NewServer(mux)
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
client := NewClient(server.URL, "user", "secret")
|
||||
client.HTTPClient = server.Client()
|
||||
|
||||
mux.HandleFunc("/Services/REST/v1/login", func(rw http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != http.MethodGet {
|
||||
http.Error(rw, fmt.Sprintf("unsupported method %s", req.Method), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
query := req.URL.Query()
|
||||
if query.Get("username") != "user" {
|
||||
http.Error(rw, fmt.Sprintf("invalid username %s", query.Get("username")), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
if query.Get("password") != "secret" {
|
||||
http.Error(rw, fmt.Sprintf("invalid password %s", query.Get("password")), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
_, _ = fmt.Fprint(rw, fakeToken)
|
||||
})
|
||||
mux.HandleFunc("/Services/REST/v1/delete", func(rw http.ResponseWriter, req *http.Request) {
|
||||
authorization := req.Header.Get(authorizationHeader)
|
||||
if authorization != fakeToken {
|
||||
http.Error(rw, fmt.Sprintf("invalid credential: %s", authorization), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
})
|
||||
client := servermock.NewBuilder[*Client](setupClient).
|
||||
Route("GET /Services/REST/v1/login",
|
||||
servermock.RawStringResponse(fakeToken),
|
||||
servermock.CheckQueryParameter().
|
||||
With("username", "user").
|
||||
With("password", "secret")).
|
||||
Route("DELETE /Services/REST/v1/delete", nil,
|
||||
servermock.CheckHeader().
|
||||
WithAuthorization(fakeToken)).
|
||||
Build(t)
|
||||
|
||||
ctx, err := client.CreateAuthenticatedContext(t.Context())
|
||||
require.NoError(t, err)
|
||||
|
|
|
|||
|
|
@ -1,58 +1,42 @@
|
|||
package internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/go-acme/lego/v4/platform/tester/servermock"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func setupTest(t *testing.T, filename string) *Client {
|
||||
t.Helper()
|
||||
func mockBuilder() *servermock.Builder[*Client] {
|
||||
return servermock.NewBuilder[*Client](
|
||||
func(server *httptest.Server) (*Client, error) {
|
||||
client, err := NewClient("user", "secret")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mux := http.NewServeMux()
|
||||
server := httptest.NewServer(mux)
|
||||
t.Cleanup(server.Close)
|
||||
client.HTTPClient = server.Client()
|
||||
client.baseURL = server.URL
|
||||
|
||||
mux.HandleFunc("GET /", func(rw http.ResponseWriter, req *http.Request) {
|
||||
username, password, ok := req.BasicAuth()
|
||||
if username != "user" || password != "secret" || !ok {
|
||||
http.Error(rw, fmt.Sprintf("auth: user %s, password %s, malformed", username, password), http.StatusOK)
|
||||
return
|
||||
}
|
||||
|
||||
file, err := os.Open(filepath.Join("fixtures", filename))
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
defer func() { _ = file.Close() }()
|
||||
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
_, err = io.Copy(rw, file)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
client, err := NewClient("user", "secret")
|
||||
require.NoError(t, err)
|
||||
|
||||
client.HTTPClient = server.Client()
|
||||
client.baseURL = server.URL
|
||||
|
||||
return client
|
||||
return client, nil
|
||||
},
|
||||
servermock.CheckHeader().
|
||||
WithBasicAuth("user", "secret"))
|
||||
}
|
||||
|
||||
func TestClient_AddRecord(t *testing.T) {
|
||||
client := setupTest(t, "add_success.txt")
|
||||
client := mockBuilder().
|
||||
Route("GET /",
|
||||
servermock.ResponseFromFixture("add_success.txt"),
|
||||
servermock.CheckQueryParameter().Strict().
|
||||
With("do", "add").
|
||||
With("hostname", "_acme-challenge.sub.example.com.").
|
||||
With("type", "txt").
|
||||
With("value", "test").
|
||||
With("ttl", "300"),
|
||||
).
|
||||
Build(t)
|
||||
|
||||
record := Record{
|
||||
Hostname: "_acme-challenge.sub.example.com.",
|
||||
|
|
@ -66,7 +50,12 @@ func TestClient_AddRecord(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_AddRecord_error(t *testing.T) {
|
||||
client := setupTest(t, "error.txt")
|
||||
client := mockBuilder().
|
||||
Route("GET /",
|
||||
servermock.ResponseFromFixture("error.txt"),
|
||||
servermock.CheckQueryParameter().
|
||||
With("do", "add")).
|
||||
Build(t)
|
||||
|
||||
record := Record{
|
||||
Hostname: "_acme-challenge.sub.example.com.",
|
||||
|
|
@ -82,7 +71,17 @@ func TestClient_AddRecord_error(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_RemoveRecord(t *testing.T) {
|
||||
client := setupTest(t, "remove_success.txt")
|
||||
client := mockBuilder().
|
||||
Route("GET /",
|
||||
servermock.ResponseFromFixture("remove_success.txt"),
|
||||
servermock.CheckQueryParameter().Strict().
|
||||
With("do", "remove").
|
||||
With("hostname", "_acme-challenge.sub.example.com.").
|
||||
With("type", "txt").
|
||||
With("value", "test").
|
||||
With("ttl", "300"),
|
||||
).
|
||||
Build(t)
|
||||
|
||||
record := Record{
|
||||
Hostname: "_acme-challenge.sub.example.com.",
|
||||
|
|
@ -96,7 +95,12 @@ func TestClient_RemoveRecord(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_RemoveRecord_error(t *testing.T) {
|
||||
client := setupTest(t, "error.txt")
|
||||
client := mockBuilder().
|
||||
Route("GET /",
|
||||
servermock.ResponseFromFixture("error.txt"),
|
||||
servermock.CheckQueryParameter().
|
||||
With("do", "remove")).
|
||||
Build(t)
|
||||
|
||||
record := Record{
|
||||
Hostname: "_acme-challenge.sub.example.com.",
|
||||
|
|
|
|||
|
|
@ -1,49 +1,42 @@
|
|||
package internal
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/go-acme/lego/v4/platform/tester/servermock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func setupTest(t *testing.T, filename string) *Client {
|
||||
t.Helper()
|
||||
func mockBuilder() *servermock.Builder[*Client] {
|
||||
return servermock.NewBuilder[*Client](
|
||||
func(server *httptest.Server) (*Client, error) {
|
||||
client, err := NewClient("user", "secret")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
file, err := os.Open(filepath.Join("fixtures", filename))
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
client.HTTPClient = server.Client()
|
||||
client.baseURL = server.URL
|
||||
|
||||
defer func() { _ = file.Close() }()
|
||||
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
_, err = io.Copy(rw, file)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}))
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
client, err := NewClient("test_user", "apiKey")
|
||||
require.NoError(t, err)
|
||||
|
||||
client.HTTPClient = server.Client()
|
||||
client.baseURL = server.URL
|
||||
|
||||
return client
|
||||
return client, nil
|
||||
},
|
||||
servermock.CheckHeader().
|
||||
WithContentTypeFromURLEncoded())
|
||||
}
|
||||
|
||||
func TestClient_StatusDomain(t *testing.T) {
|
||||
client := setupTest(t, "status-domain.json")
|
||||
client := mockBuilder().
|
||||
Route("POST /", servermock.ResponseFromFixture("status-domain.json"),
|
||||
servermock.CheckForm().Strict().
|
||||
WithRegexp("signature", "[a-z0-9]+").
|
||||
WithRegexp("timestamp", `\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z`).
|
||||
With("command", "statusDomain").
|
||||
With("user", "user").
|
||||
With("domain", "example.com"),
|
||||
).
|
||||
Build(t)
|
||||
|
||||
domain, err := client.StatusDomain(t.Context(), "example.com")
|
||||
require.NoError(t, err)
|
||||
|
|
@ -79,14 +72,26 @@ func TestClient_StatusDomain(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_StatusDomain_error(t *testing.T) {
|
||||
client := setupTest(t, "error.json")
|
||||
client := mockBuilder().
|
||||
Route("POST /", servermock.ResponseFromFixture("error.json")).
|
||||
Build(t)
|
||||
|
||||
_, err := client.StatusDomain(t.Context(), "example.com")
|
||||
require.ErrorIs(t, err, APIError{Code: 402, Status: "error", Message: "Invalid user."})
|
||||
}
|
||||
|
||||
func TestClient_ListRecords(t *testing.T) {
|
||||
client := setupTest(t, "list-records.json")
|
||||
client := mockBuilder().
|
||||
Route("POST /", servermock.ResponseFromFixture("list-records.json"),
|
||||
servermock.CheckForm().Strict().
|
||||
WithRegexp("signature", "[a-z0-9]+").
|
||||
WithRegexp("timestamp", `\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z`).
|
||||
With("account", "example").
|
||||
With("command", "listDNSRR").
|
||||
With("user", "user").
|
||||
With("dnszone", "example.com"),
|
||||
).
|
||||
Build(t)
|
||||
|
||||
resp, err := client.ListRecords(t.Context(), "example", "example.com")
|
||||
require.NoError(t, err)
|
||||
|
|
@ -105,14 +110,28 @@ func TestClient_ListRecords(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_ListRecords_error(t *testing.T) {
|
||||
client := setupTest(t, "error.json")
|
||||
client := mockBuilder().
|
||||
Route("POST /", servermock.ResponseFromFixture("error.json")).
|
||||
Build(t)
|
||||
|
||||
_, err := client.ListRecords(t.Context(), "example", "example.com")
|
||||
require.ErrorIs(t, err, APIError{Code: 402, Status: "error", Message: "Invalid user."})
|
||||
}
|
||||
|
||||
func TestClient_AddRecord(t *testing.T) {
|
||||
client := setupTest(t, "add-record.json")
|
||||
client := mockBuilder().
|
||||
Route("POST /", servermock.ResponseFromFixture("add-record.json"),
|
||||
servermock.CheckForm().Strict().
|
||||
WithRegexp("signature", "[a-z0-9]+").
|
||||
WithRegexp("timestamp", `\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z`).
|
||||
With("account", "test").
|
||||
With("command", "addDNSRR").
|
||||
With("key", "2565").
|
||||
With("user", "user").
|
||||
With("rrdata", "example.com 600 IN TXT txttxttxt").
|
||||
With("dnszone", "example.com"),
|
||||
).
|
||||
Build(t)
|
||||
|
||||
testRecord := Record{
|
||||
ID: 2565,
|
||||
|
|
@ -139,7 +158,9 @@ func TestClient_AddRecord(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_AddRecord_error(t *testing.T) {
|
||||
client := setupTest(t, "error.json")
|
||||
client := mockBuilder().
|
||||
Route("POST /", servermock.ResponseFromFixture("error.json")).
|
||||
Build(t)
|
||||
|
||||
testRecord := Record{
|
||||
ID: 2565,
|
||||
|
|
@ -154,14 +175,28 @@ func TestClient_AddRecord_error(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_DeleteRecord(t *testing.T) {
|
||||
client := setupTest(t, "delete-record.json")
|
||||
client := mockBuilder().
|
||||
Route("POST /", servermock.ResponseFromFixture("delete-record.json"),
|
||||
servermock.CheckForm().Strict().
|
||||
WithRegexp("signature", "[a-z0-9]+").
|
||||
WithRegexp("timestamp", `\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z`).
|
||||
With("account", "test").
|
||||
With("command", "deleteDNSRR").
|
||||
With("key", "2374").
|
||||
With("user", "user").
|
||||
With("rrdata", "example.com 600 IN TXT txttxttxt").
|
||||
With("dnszone", "example.com"),
|
||||
).
|
||||
Build(t)
|
||||
|
||||
err := client.DeleteRecord(t.Context(), "example.com", "test", "example.com 600 IN TXT txttxttxt", "2374")
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestClient_DeleteRecord_error(t *testing.T) {
|
||||
client := setupTest(t, "error.json")
|
||||
client := mockBuilder().
|
||||
Route("POST /", servermock.ResponseFromFixture("error.json")).
|
||||
Build(t)
|
||||
|
||||
err := client.DeleteRecord(t.Context(), "example.com", "test", "example.com 600 IN TXT txttxttxt", "2374")
|
||||
require.ErrorIs(t, err, APIError{Code: 402, Status: "error", Message: "Invalid user."})
|
||||
|
|
|
|||
|
|
@ -1,70 +1,38 @@
|
|||
package internal
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/go-acme/lego/v4/challenge/dns01"
|
||||
"github.com/go-acme/lego/v4/platform/tester/servermock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func setupTest(t *testing.T) (*Client, *http.ServeMux) {
|
||||
t.Helper()
|
||||
func mockBuilder() *servermock.Builder[*Client] {
|
||||
return servermock.NewBuilder[*Client](
|
||||
func(server *httptest.Server) (*Client, error) {
|
||||
client := NewClient(OAuthStaticAccessToken(server.Client(), "secret"))
|
||||
client.BaseURL, _ = url.Parse(server.URL)
|
||||
|
||||
mux := http.NewServeMux()
|
||||
server := httptest.NewServer(mux)
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
client := NewClient(OAuthStaticAccessToken(server.Client(), "secret"))
|
||||
client.BaseURL, _ = url.Parse(server.URL)
|
||||
|
||||
return client, mux
|
||||
}
|
||||
|
||||
func checkAuthorizationHeader(req *http.Request) error {
|
||||
val := req.Header.Get("Authorization")
|
||||
if val != "Bearer secret" {
|
||||
return fmt.Errorf("invalid header value, got: %s want %s", val, "Bearer secret")
|
||||
}
|
||||
return nil
|
||||
return client, nil
|
||||
},
|
||||
servermock.CheckHeader().WithJSONHeaders().
|
||||
WithAuthorization("Bearer secret"))
|
||||
}
|
||||
|
||||
func TestClient_GetDomainIDByName(t *testing.T) {
|
||||
client, mux := setupTest(t)
|
||||
|
||||
mux.HandleFunc("/v1/domains", func(rw http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != http.MethodGet {
|
||||
http.Error(rw, "invalid method: "+req.Method, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
err := checkAuthorizationHeader(req)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
domainList := DomainListingResponse{
|
||||
Embedded: EmbeddedDomainList{Domains: []*Domain{
|
||||
{ID: 1, Name: "test.com"},
|
||||
{ID: 2, Name: "test.org"},
|
||||
}},
|
||||
}
|
||||
|
||||
err = json.NewEncoder(rw).Encode(domainList)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
})
|
||||
client := mockBuilder().
|
||||
Route("GET /v1/domains",
|
||||
servermock.JSONEncode(DomainListingResponse{
|
||||
Embedded: EmbeddedDomainList{Domains: []*Domain{
|
||||
{ID: 1, Name: "test.com"},
|
||||
{ID: 2, Name: "test.org"},
|
||||
}},
|
||||
})).
|
||||
Build(t)
|
||||
|
||||
id, err := client.GetDomainIDByName(t.Context(), "test.com")
|
||||
require.NoError(t, err)
|
||||
|
|
@ -73,65 +41,26 @@ func TestClient_GetDomainIDByName(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_CheckNameservers(t *testing.T) {
|
||||
client, mux := setupTest(t)
|
||||
|
||||
mux.HandleFunc("/v1/domains/1/nameservers", func(rw http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != http.MethodGet {
|
||||
http.Error(rw, "invalid method: "+req.Method, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
err := checkAuthorizationHeader(req)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
nsResp := NameserverResponse{
|
||||
Nameservers: []*Nameserver{
|
||||
{Name: ns1},
|
||||
{Name: ns2},
|
||||
// {Name: "ns.fake.de"},
|
||||
},
|
||||
}
|
||||
|
||||
err = json.NewEncoder(rw).Encode(nsResp)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
})
|
||||
client := mockBuilder().
|
||||
Route("GET /v1/domains/1/nameservers",
|
||||
servermock.JSONEncode(NameserverResponse{
|
||||
Nameservers: []*Nameserver{
|
||||
{Name: ns1},
|
||||
{Name: ns2},
|
||||
// {Name: "ns.fake.de"},
|
||||
},
|
||||
})).
|
||||
Build(t)
|
||||
|
||||
err := client.CheckNameservers(t.Context(), 1)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestClient_CreateRecord(t *testing.T) {
|
||||
client, mux := setupTest(t)
|
||||
|
||||
mux.HandleFunc("/v1/domains/1/nameservers/records", func(rw http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != http.MethodPost {
|
||||
http.Error(rw, "invalid method: "+req.Method, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
err := checkAuthorizationHeader(req)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
content, err := io.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if string(bytes.TrimSpace(content)) != `{"name":"test.com","value":"value","ttl":300,"priority":0,"type":"TXT"}` {
|
||||
http.Error(rw, "invalid request body: "+string(content), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
})
|
||||
client := mockBuilder().
|
||||
Route("POST /v1/domains/1/nameservers/records", nil,
|
||||
servermock.CheckRequestJSONBodyFromFile("create_record-request.json")).
|
||||
Build(t)
|
||||
|
||||
record := &Record{
|
||||
Name: "test.com",
|
||||
|
|
@ -145,114 +74,44 @@ func TestClient_CreateRecord(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_DeleteTXTRecord(t *testing.T) {
|
||||
client, mux := setupTest(t)
|
||||
|
||||
domainName := "lego.test"
|
||||
recordValue := "test"
|
||||
|
||||
records := []*Record{
|
||||
{
|
||||
Name: "_acme-challenge",
|
||||
Value: recordValue,
|
||||
Type: "TXT",
|
||||
},
|
||||
{
|
||||
Name: "_acme-challenge",
|
||||
Value: recordValue,
|
||||
Type: "A",
|
||||
},
|
||||
{
|
||||
Name: "foobar",
|
||||
Value: recordValue,
|
||||
Type: "TXT",
|
||||
},
|
||||
}
|
||||
|
||||
expectedRecords := []*Record{
|
||||
{
|
||||
Name: "_acme-challenge",
|
||||
Value: recordValue,
|
||||
Type: "A",
|
||||
},
|
||||
{
|
||||
Name: "foobar",
|
||||
Value: recordValue,
|
||||
Type: "TXT",
|
||||
},
|
||||
}
|
||||
|
||||
mux.HandleFunc("/v1/domains/1", func(rw http.ResponseWriter, req *http.Request) {
|
||||
err := checkAuthorizationHeader(req)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
resp := DomainResponse{
|
||||
ID: 1,
|
||||
Name: domainName,
|
||||
}
|
||||
|
||||
err = json.NewEncoder(rw).Encode(resp)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
mux.HandleFunc("/v1/domains/1/nameservers", func(rw http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != http.MethodGet {
|
||||
http.Error(rw, "invalid method: "+req.Method, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
nsResp := NameserverResponse{
|
||||
Nameservers: []*Nameserver{{Name: ns1}, {Name: ns2}},
|
||||
}
|
||||
|
||||
err := json.NewEncoder(rw).Encode(nsResp)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
mux.HandleFunc("/v1/domains/1/nameservers/records", func(rw http.ResponseWriter, req *http.Request) {
|
||||
switch req.Method {
|
||||
case http.MethodGet:
|
||||
resp := RecordListingResponse{
|
||||
client := mockBuilder().
|
||||
Route("GET /v1/domains/",
|
||||
servermock.JSONEncode(DomainResponse{
|
||||
ID: 1,
|
||||
Name: domainName,
|
||||
})).
|
||||
Route("GET /v1/domains/1/nameservers",
|
||||
servermock.JSONEncode(NameserverResponse{
|
||||
Nameservers: []*Nameserver{{Name: ns1}, {Name: ns2}},
|
||||
})).
|
||||
Route("GET /v1/domains/1/nameservers/records",
|
||||
servermock.JSONEncode(RecordListingResponse{
|
||||
Embedded: EmbeddedRecordList{
|
||||
Records: records,
|
||||
Records: []*Record{
|
||||
{
|
||||
Name: "_acme-challenge",
|
||||
Value: recordValue,
|
||||
Type: "TXT",
|
||||
},
|
||||
{
|
||||
Name: "_acme-challenge",
|
||||
Value: recordValue,
|
||||
Type: "A",
|
||||
},
|
||||
{
|
||||
Name: "foobar",
|
||||
Value: recordValue,
|
||||
Type: "TXT",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err := json.NewEncoder(rw).Encode(resp)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
case http.MethodPut:
|
||||
var records []*Record
|
||||
err := json.NewDecoder(req.Body).Decode(&records)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if len(records) == 0 {
|
||||
http.Error(rw, "empty request body", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(expectedRecords, records) {
|
||||
http.Error(rw, fmt.Sprintf("invalid records: %v", records), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
default:
|
||||
http.Error(rw, "invalid method: "+req.Method, http.StatusBadRequest)
|
||||
}
|
||||
})
|
||||
})).
|
||||
Route("PUT /v1/domains/1/nameservers/records", nil,
|
||||
servermock.CheckRequestJSONBodyFromFile("delete_txt_record-request.json")).
|
||||
Build(t)
|
||||
|
||||
info := dns01.GetChallengeInfo(domainName, "abc")
|
||||
err := client.DeleteTXTRecord(t.Context(), 1, info.EffectiveFQDN, recordValue)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"name": "test.com",
|
||||
"value": "value",
|
||||
"ttl": 300,
|
||||
"priority": 0,
|
||||
"type": "TXT"
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
[
|
||||
{
|
||||
"name": "_acme-challenge",
|
||||
"value": "test",
|
||||
"ttl": 0,
|
||||
"priority": 0,
|
||||
"type": "A"
|
||||
},
|
||||
{
|
||||
"name": "foobar",
|
||||
"value": "test",
|
||||
"ttl": 0,
|
||||
"priority": 0,
|
||||
"type": "TXT"
|
||||
}
|
||||
]
|
||||
|
|
@ -1,127 +1,63 @@
|
|||
package internal
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/go-acme/lego/v4/platform/tester/servermock"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func setupTest(t *testing.T) (*Client, *http.ServeMux) {
|
||||
t.Helper()
|
||||
func mockBuilder() *servermock.Builder[*Client] {
|
||||
return servermock.NewBuilder[*Client](
|
||||
func(server *httptest.Server) (*Client, error) {
|
||||
client := NewClient("clientID", "email@example.com", "secret", 300)
|
||||
client.HTTPClient = server.Client()
|
||||
client.apiBaseURL, _ = url.Parse(server.URL + "/api")
|
||||
client.loginURL, _ = url.Parse(server.URL + "/login")
|
||||
|
||||
mux := http.NewServeMux()
|
||||
server := httptest.NewServer(mux)
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
client := NewClient("clientID", "email@example.com", "secret", 300)
|
||||
client.HTTPClient = server.Client()
|
||||
client.apiBaseURL, _ = url.Parse(server.URL + "/api")
|
||||
client.loginURL, _ = url.Parse(server.URL + "/login")
|
||||
|
||||
return client, mux
|
||||
return client, nil
|
||||
},
|
||||
servermock.CheckHeader().WithJSONHeaders(),
|
||||
)
|
||||
}
|
||||
|
||||
func TestClient_AddRecord(t *testing.T) {
|
||||
client, mux := setupTest(t)
|
||||
client := mockBuilder().
|
||||
Route("POST /api/domain/search",
|
||||
servermock.ResponseFromFixture("domain_search.json"),
|
||||
servermock.CheckRequestJSONBodyFromFile("domain_search-request.json")).
|
||||
Route("POST /api/record-txt", nil,
|
||||
servermock.CheckRequestJSONBodyFromFile("record_txt-request.json")).
|
||||
Route("PUT /api/domain/A/publish", nil,
|
||||
servermock.CheckRequestJSONBodyFromFile("publish-request.json")).
|
||||
Route("POST /login",
|
||||
servermock.ResponseFromFixture("login.json"),
|
||||
servermock.CheckRequestJSONBodyFromFile("login-request.json")).
|
||||
Build(t)
|
||||
|
||||
mux.HandleFunc("/api/domain/search", func(rw http.ResponseWriter, req *http.Request) {
|
||||
response := SearchResponse{
|
||||
Items: []Domain{
|
||||
{
|
||||
ID: "A",
|
||||
DomainName: "example.com",
|
||||
},
|
||||
},
|
||||
}
|
||||
ctx, err := client.CreateAuthenticatedContext(t.Context())
|
||||
require.NoError(t, err)
|
||||
|
||||
err := json.NewEncoder(rw).Encode(response)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
})
|
||||
mux.HandleFunc("/api/record-txt", func(rw http.ResponseWriter, req *http.Request) {})
|
||||
mux.HandleFunc("/api/domain/A/publish", func(rw http.ResponseWriter, req *http.Request) {})
|
||||
mux.HandleFunc("/login", func(rw http.ResponseWriter, req *http.Request) {
|
||||
response := AuthResponse{
|
||||
Auth: Auth{
|
||||
AccessToken: "at",
|
||||
RefreshToken: "",
|
||||
},
|
||||
}
|
||||
|
||||
err := json.NewEncoder(rw).Encode(response)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
err := client.AddRecord(t.Context(), "example.com", "_acme-challenge.example.com", "txt")
|
||||
err = client.AddRecord(ctx, "example.com", "_acme-challenge.example.com", "txt")
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestClient_DeleteRecord(t *testing.T) {
|
||||
client, mux := setupTest(t)
|
||||
|
||||
mux.HandleFunc("/api/domain/search", func(rw http.ResponseWriter, req *http.Request) {
|
||||
response := SearchResponse{
|
||||
Items: []Domain{
|
||||
{
|
||||
ID: "A",
|
||||
DomainName: "example.com",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err := json.NewEncoder(rw).Encode(response)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
})
|
||||
mux.HandleFunc("/api/domain/A", func(rw http.ResponseWriter, req *http.Request) {
|
||||
response := DomainInfo{
|
||||
ID: "Z",
|
||||
DomainName: "example.com",
|
||||
LastDomainRecordList: []Record{
|
||||
{
|
||||
ID: "R01",
|
||||
DomainID: "A",
|
||||
Name: "_acme-challenge.example.com",
|
||||
Value: "txt",
|
||||
Type: "TXT",
|
||||
},
|
||||
},
|
||||
SoaTTL: 300,
|
||||
}
|
||||
|
||||
err := json.NewEncoder(rw).Encode(response)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
})
|
||||
mux.HandleFunc("/api/record/R01", func(rw http.ResponseWriter, req *http.Request) {})
|
||||
mux.HandleFunc("/api/domain/A/publish", func(rw http.ResponseWriter, req *http.Request) {})
|
||||
mux.HandleFunc("/login", func(rw http.ResponseWriter, req *http.Request) {
|
||||
response := AuthResponse{
|
||||
Auth: Auth{
|
||||
AccessToken: "at",
|
||||
RefreshToken: "",
|
||||
},
|
||||
}
|
||||
|
||||
err := json.NewEncoder(rw).Encode(response)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
})
|
||||
client := mockBuilder().
|
||||
Route("POST /api/domain/search",
|
||||
servermock.ResponseFromFixture("domain_search.json"),
|
||||
servermock.CheckRequestJSONBodyFromFile("domain_search-request.json")).
|
||||
Route("GET /api/domain/A",
|
||||
servermock.ResponseFromFixture("domain-request.json")).
|
||||
Route("DELETE /api/record/R01", nil).
|
||||
Route("PUT /api/domain/A/publish", nil,
|
||||
servermock.CheckRequestJSONBodyFromFile("publish-request.json")).
|
||||
Route("POST /login",
|
||||
servermock.ResponseFromFixture("login.json"),
|
||||
servermock.CheckRequestJSONBodyFromFile("login-request.json")).
|
||||
Build(t)
|
||||
|
||||
ctx, err := client.CreateAuthenticatedContext(t.Context())
|
||||
require.NoError(t, err)
|
||||
|
|
|
|||
14
providers/dns/clouddns/internal/fixtures/domain-request.json
Normal file
14
providers/dns/clouddns/internal/fixtures/domain-request.json
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"id": "Z",
|
||||
"domainName": "example.com",
|
||||
"lastDomainRecordList": [
|
||||
{
|
||||
"id": "R01",
|
||||
"domainId": "A",
|
||||
"name": "_acme-challenge.example.com",
|
||||
"value": "txt",
|
||||
"type": "TXT"
|
||||
}
|
||||
],
|
||||
"soaTtl": 300
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"search": [
|
||||
{
|
||||
"name": "clientId",
|
||||
"operator": "eq",
|
||||
"value": "clientID"
|
||||
},
|
||||
{
|
||||
"name": "domainName",
|
||||
"operator": "eq",
|
||||
"value": "example.com"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"items": [
|
||||
{
|
||||
"id": "A",
|
||||
"domainName": "example.com"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"email": "email@example.com",
|
||||
"password": "secret"
|
||||
}
|
||||
5
providers/dns/clouddns/internal/fixtures/login.json
Normal file
5
providers/dns/clouddns/internal/fixtures/login.json
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"auth": {
|
||||
"accessToken": "at"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"soaTtl": 300
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"domainId": "A",
|
||||
"name": "_acme-challenge.example.com",
|
||||
"value": "txt",
|
||||
"type": "TXT"
|
||||
}
|
||||
|
|
@ -1,38 +1,20 @@
|
|||
package internal
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/go-acme/lego/v4/platform/tester/servermock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestClient_CreateAuthenticatedContext(t *testing.T) {
|
||||
client, mux := setupTest(t)
|
||||
|
||||
mux.HandleFunc("/login", func(rw http.ResponseWriter, req *http.Request) {
|
||||
response := AuthResponse{
|
||||
Auth: Auth{
|
||||
AccessToken: "at",
|
||||
RefreshToken: "",
|
||||
},
|
||||
}
|
||||
|
||||
err := json.NewEncoder(rw).Encode(response)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
})
|
||||
mux.HandleFunc("/api/record/xxx", func(rw http.ResponseWriter, req *http.Request) {
|
||||
authorization := req.Header.Get(authorizationHeader)
|
||||
if authorization != "Bearer at" {
|
||||
http.Error(rw, "invalid credential: "+authorization, http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
})
|
||||
client := mockBuilder().
|
||||
Route("POST /login",
|
||||
servermock.ResponseFromFixture("login.json"),
|
||||
servermock.CheckRequestJSONBodyFromFile("login-request.json")).
|
||||
Route("DELETE /api/record/xxx", nil).
|
||||
Build(t)
|
||||
|
||||
ctx, err := client.CreateAuthenticatedContext(t.Context())
|
||||
require.NoError(t, err)
|
||||
|
|
|
|||
|
|
@ -1,43 +1,25 @@
|
|||
package internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/go-acme/lego/v4/platform/tester/servermock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func setupTest(t *testing.T, subAuthID string, handler http.HandlerFunc) *Client {
|
||||
t.Helper()
|
||||
|
||||
server := httptest.NewServer(handler)
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
client, err := NewClient("myAuthID", subAuthID, "myAuthPassword")
|
||||
require.NoError(t, err)
|
||||
|
||||
client.BaseURL, _ = url.Parse(server.URL)
|
||||
client.HTTPClient = server.Client()
|
||||
|
||||
return client
|
||||
}
|
||||
|
||||
func handlerMock(method string, jsonData []byte) http.HandlerFunc {
|
||||
return func(rw http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != method {
|
||||
http.Error(rw, "Incorrect method used", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
_, err := rw.Write(jsonData)
|
||||
func setupClient(subAuthID string) func(server *httptest.Server) (*Client, error) {
|
||||
return func(server *httptest.Server) (*Client, error) {
|
||||
client, err := NewClient("myAuthID", subAuthID, "myAuthPassword")
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client.BaseURL, _ = url.Parse(server.URL)
|
||||
client.HTTPClient = server.Client()
|
||||
return client, nil
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -131,7 +113,15 @@ func TestClient_GetZone(t *testing.T) {
|
|||
|
||||
for _, test := range testCases {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
client := setupTest(t, "", handlerMock(http.MethodGet, []byte(test.apiResponse)))
|
||||
client := servermock.NewBuilder[*Client](setupClient("")).
|
||||
Route("GET /get-zone-info.json",
|
||||
servermock.RawStringResponse(test.apiResponse),
|
||||
servermock.CheckQueryParameter().Strict().
|
||||
With("auth-id", "myAuthID").
|
||||
With("auth-password", "myAuthPassword").
|
||||
With("domain-name", "foo.com"),
|
||||
).
|
||||
Build(t)
|
||||
|
||||
zone, err := client.GetZone(t.Context(), test.authFQDN)
|
||||
|
||||
|
|
@ -238,7 +228,17 @@ func TestClient_FindTxtRecord(t *testing.T) {
|
|||
|
||||
for _, test := range testCases {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
client := setupTest(t, "", handlerMock(http.MethodGet, []byte(test.apiResponse)))
|
||||
client := servermock.NewBuilder[*Client](setupClient("")).
|
||||
Route("GET /records.json",
|
||||
servermock.RawStringResponse(test.apiResponse),
|
||||
servermock.CheckQueryParameter().Strict().
|
||||
With("auth-id", "myAuthID").
|
||||
With("auth-password", "myAuthPassword").
|
||||
With("type", "TXT").
|
||||
With("host", "_acme-challenge").
|
||||
With("domain-name", test.zoneName),
|
||||
).
|
||||
Build(t)
|
||||
|
||||
txtRecord, err := client.FindTxtRecord(t.Context(), test.zoneName, test.authFQDN)
|
||||
|
||||
|
|
@ -347,7 +347,17 @@ func TestClient_ListTxtRecord(t *testing.T) {
|
|||
|
||||
for _, test := range testCases {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
client := setupTest(t, "", handlerMock(http.MethodGet, []byte(test.apiResponse)))
|
||||
client := servermock.NewBuilder[*Client](setupClient("")).
|
||||
Route("GET /records.json",
|
||||
servermock.RawStringResponse(test.apiResponse),
|
||||
servermock.CheckQueryParameter().Strict().
|
||||
With("auth-id", "myAuthID").
|
||||
With("auth-password", "myAuthPassword").
|
||||
With("type", "TXT").
|
||||
With("host", "_acme-challenge").
|
||||
With("domain-name", test.zoneName),
|
||||
).
|
||||
Build(t)
|
||||
|
||||
txtRecords, err := client.ListTxtRecords(t.Context(), test.zoneName, test.authFQDN)
|
||||
|
||||
|
|
@ -363,7 +373,7 @@ func TestClient_ListTxtRecord(t *testing.T) {
|
|||
|
||||
func TestClient_AddTxtRecord(t *testing.T) {
|
||||
type expected struct {
|
||||
query string
|
||||
query url.Values
|
||||
errorMsg string
|
||||
}
|
||||
|
||||
|
|
@ -387,7 +397,15 @@ func TestClient_AddTxtRecord(t *testing.T) {
|
|||
ttl: 60,
|
||||
apiResponse: `{"status":"Success","statusDescription":"The record was added successfully."}`,
|
||||
expected: expected{
|
||||
query: `auth-id=myAuthID&auth-password=myAuthPassword&domain-name=example.com&host=_acme-challenge.foo&record=txtTXTtxtTXTtxtTXTtxtTXT&record-type=TXT&ttl=60`,
|
||||
query: url.Values{
|
||||
"auth-id": {"myAuthID"},
|
||||
"auth-password": {"myAuthPassword"},
|
||||
"domain-name": {"example.com"},
|
||||
"host": {"_acme-challenge.foo"},
|
||||
"record": {"txtTXTtxtTXTtxtTXTtxtTXT"},
|
||||
"record-type": {"TXT"},
|
||||
"ttl": {"60"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -399,7 +417,15 @@ func TestClient_AddTxtRecord(t *testing.T) {
|
|||
ttl: 60,
|
||||
apiResponse: `{"status":"Success","statusDescription":"The record was added successfully."}`,
|
||||
expected: expected{
|
||||
query: `auth-id=myAuthID&auth-password=myAuthPassword&domain-name=example.com&host=_acme-challenge&record=TXTtxtTXTtxtTXTtxtTXTtxt&record-type=TXT&ttl=60`,
|
||||
query: url.Values{
|
||||
"auth-id": {"myAuthID"},
|
||||
"auth-password": {"myAuthPassword"},
|
||||
"domain-name": {"example.com"},
|
||||
"host": {"_acme-challenge"},
|
||||
"record": {"TXTtxtTXTtxtTXTtxtTXTtxt"},
|
||||
"record-type": {"TXT"},
|
||||
"ttl": {"60"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -411,7 +437,15 @@ func TestClient_AddTxtRecord(t *testing.T) {
|
|||
ttl: 60,
|
||||
apiResponse: `{"status":"Success","statusDescription":"The record was added successfully."}`,
|
||||
expected: expected{
|
||||
query: `auth-password=myAuthPassword&domain-name=example.com&host=_acme-challenge&record=TXTtxtTXTtxtTXTtxtTXTtxt&record-type=TXT&sub-auth-id=mySubAuthID&ttl=60`,
|
||||
query: url.Values{
|
||||
"auth-password": {"myAuthPassword"},
|
||||
"domain-name": {"example.com"},
|
||||
"host": {"_acme-challenge"},
|
||||
"record": {"TXTtxtTXTtxtTXTtxtTXTtxt"},
|
||||
"record-type": {"TXT"},
|
||||
"sub-auth-id": {"mySubAuthID"},
|
||||
"ttl": {"60"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -423,7 +457,15 @@ func TestClient_AddTxtRecord(t *testing.T) {
|
|||
ttl: 120,
|
||||
apiResponse: `{"status":"Failed","statusDescription":"Invalid TTL. Choose from the list of the values we support."}`,
|
||||
expected: expected{
|
||||
query: `auth-id=myAuthID&auth-password=myAuthPassword&domain-name=example.com&host=_acme-challenge&record=TXTtxtTXTtxtTXTtxtTXTtxt&record-type=TXT&ttl=300`,
|
||||
query: url.Values{
|
||||
"auth-id": {"myAuthID"},
|
||||
"auth-password": {"myAuthPassword"},
|
||||
"domain-name": {"example.com"},
|
||||
"host": {"_acme-challenge"},
|
||||
"record": {"TXTtxtTXTtxtTXTtxtTXTtxt"},
|
||||
"record-type": {"TXT"},
|
||||
"ttl": {"300"},
|
||||
},
|
||||
errorMsg: "failed to add TXT record: Failed Invalid TTL. Choose from the list of the values we support.",
|
||||
},
|
||||
},
|
||||
|
|
@ -436,7 +478,15 @@ func TestClient_AddTxtRecord(t *testing.T) {
|
|||
ttl: 120,
|
||||
apiResponse: `[{}]`,
|
||||
expected: expected{
|
||||
query: `auth-id=myAuthID&auth-password=myAuthPassword&domain-name=example.com&host=_acme-challenge&record=TXTtxtTXTtxtTXTtxtTXTtxt&record-type=TXT&ttl=300`,
|
||||
query: url.Values{
|
||||
"auth-id": {"myAuthID"},
|
||||
"auth-password": {"myAuthPassword"},
|
||||
"domain-name": {"example.com"},
|
||||
"host": {"_acme-challenge"},
|
||||
"record": {"TXTtxtTXTtxtTXTtxtTXTtxt"},
|
||||
"record-type": {"TXT"},
|
||||
"ttl": {"300"},
|
||||
},
|
||||
errorMsg: "unable to unmarshal response: [status code: 200] body: [{}] error: json: cannot unmarshal array into Go value of type internal.apiResponse",
|
||||
},
|
||||
},
|
||||
|
|
@ -444,15 +494,13 @@ func TestClient_AddTxtRecord(t *testing.T) {
|
|||
|
||||
for _, test := range testCases {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
client := setupTest(t, test.subAuthID, func(rw http.ResponseWriter, req *http.Request) {
|
||||
if test.expected.query != req.URL.RawQuery {
|
||||
msg := fmt.Sprintf("got: %s, want: %s", test.expected.query, req.URL.RawQuery)
|
||||
http.Error(rw, msg, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
handlerMock(http.MethodPost, []byte(test.apiResponse))(rw, req)
|
||||
})
|
||||
client := servermock.NewBuilder[*Client](setupClient(test.subAuthID)).
|
||||
Route("POST /add-record.json",
|
||||
servermock.RawStringResponse(test.apiResponse),
|
||||
servermock.CheckQueryParameter().Strict().
|
||||
WithValues(test.expected.query),
|
||||
).
|
||||
Build(t)
|
||||
|
||||
err := client.AddTxtRecord(t.Context(), test.zoneName, test.authFQDN, test.value, test.ttl)
|
||||
|
||||
|
|
@ -467,7 +515,7 @@ func TestClient_AddTxtRecord(t *testing.T) {
|
|||
|
||||
func TestClient_RemoveTxtRecord(t *testing.T) {
|
||||
type expected struct {
|
||||
query string
|
||||
query url.Values
|
||||
errorMsg string
|
||||
}
|
||||
|
||||
|
|
@ -484,7 +532,12 @@ func TestClient_RemoveTxtRecord(t *testing.T) {
|
|||
zoneName: "foo.com",
|
||||
apiResponse: `{ "status": "Success", "statusDescription": "The record was deleted successfully." }`,
|
||||
expected: expected{
|
||||
query: `auth-id=myAuthID&auth-password=myAuthPassword&domain-name=foo.com&record-id=5769228`,
|
||||
query: url.Values{
|
||||
"auth-id": {"myAuthID"},
|
||||
"auth-password": {"myAuthPassword"},
|
||||
"domain-name": {"foo.com"},
|
||||
"record-id": {"5769228"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -493,7 +546,12 @@ func TestClient_RemoveTxtRecord(t *testing.T) {
|
|||
zoneName: "foo.com",
|
||||
apiResponse: `{ "status": "Failed", "statusDescription": "Invalid record-id param." }`,
|
||||
expected: expected{
|
||||
query: `auth-id=myAuthID&auth-password=myAuthPassword&domain-name=foo.com&record-id=5769000`,
|
||||
query: url.Values{
|
||||
"auth-id": {"myAuthID"},
|
||||
"auth-password": {"myAuthPassword"},
|
||||
"domain-name": {"foo.com"},
|
||||
"record-id": {"5769000"},
|
||||
},
|
||||
errorMsg: "failed to remove TXT record: Failed Invalid record-id param.",
|
||||
},
|
||||
},
|
||||
|
|
@ -503,7 +561,12 @@ func TestClient_RemoveTxtRecord(t *testing.T) {
|
|||
zoneName: "foo-plus.com",
|
||||
apiResponse: `[{}]`,
|
||||
expected: expected{
|
||||
query: `auth-id=myAuthID&auth-password=myAuthPassword&domain-name=foo-plus.com&record-id=44`,
|
||||
query: url.Values{
|
||||
"auth-id": {"myAuthID"},
|
||||
"auth-password": {"myAuthPassword"},
|
||||
"domain-name": {"foo-plus.com"},
|
||||
"record-id": {"44"},
|
||||
},
|
||||
errorMsg: "unable to unmarshal response: [status code: 200] body: [{}] error: json: cannot unmarshal array into Go value of type internal.apiResponse",
|
||||
},
|
||||
},
|
||||
|
|
@ -511,23 +574,15 @@ func TestClient_RemoveTxtRecord(t *testing.T) {
|
|||
|
||||
for _, test := range testCases {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
if test.expected.query != req.URL.RawQuery {
|
||||
msg := fmt.Sprintf("got: %s, want: %s", test.expected.query, req.URL.RawQuery)
|
||||
http.Error(rw, msg, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
client := servermock.NewBuilder[*Client](setupClient("")).
|
||||
Route("POST /delete-record.json",
|
||||
servermock.RawStringResponse(test.apiResponse),
|
||||
servermock.CheckQueryParameter().Strict().
|
||||
WithValues(test.expected.query),
|
||||
).
|
||||
Build(t)
|
||||
|
||||
handlerMock(http.MethodPost, []byte(test.apiResponse))(rw, req)
|
||||
}))
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
client, err := NewClient("myAuthID", "", "myAuthPassword")
|
||||
require.NoError(t, err)
|
||||
|
||||
client.BaseURL, _ = url.Parse(server.URL)
|
||||
|
||||
err = client.RemoveTxtRecord(t.Context(), test.id, test.zoneName)
|
||||
err := client.RemoveTxtRecord(t.Context(), test.id, test.zoneName)
|
||||
|
||||
if test.expected.errorMsg != "" {
|
||||
require.EqualError(t, err, test.expected.errorMsg)
|
||||
|
|
@ -589,13 +644,15 @@ func TestClient_GetUpdateStatus(t *testing.T) {
|
|||
|
||||
for _, test := range testCases {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
server := httptest.NewServer(handlerMock(http.MethodGet, []byte(test.apiResponse)))
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
client, err := NewClient("myAuthID", "", "myAuthPassword")
|
||||
require.NoError(t, err)
|
||||
|
||||
client.BaseURL, _ = url.Parse(server.URL)
|
||||
client := servermock.NewBuilder[*Client](setupClient("")).
|
||||
Route("GET /update-status.json",
|
||||
servermock.RawStringResponse(test.apiResponse),
|
||||
servermock.CheckQueryParameter().Strict().
|
||||
With("auth-id", "myAuthID").
|
||||
With("auth-password", "myAuthPassword").
|
||||
With("domain-name", test.zoneName),
|
||||
).
|
||||
Build(t)
|
||||
|
||||
syncProgress, err := client.GetUpdateStatus(t.Context(), test.zoneName)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,62 +1,40 @@
|
|||
package internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/platform/tester/servermock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func setupTest(t *testing.T, pattern string, handler http.HandlerFunc) *Client {
|
||||
t.Helper()
|
||||
func mockBuilder() *servermock.Builder[*Client] {
|
||||
return servermock.NewBuilder[*Client](
|
||||
func(server *httptest.Server) (*Client, error) {
|
||||
client := NewClient("user", "secret")
|
||||
client.HTTPClient = server.Client()
|
||||
client.APIEndpoint, _ = url.Parse(server.URL)
|
||||
client.token = &Token{
|
||||
AccessToken: "secret",
|
||||
ExpiresIn: 60,
|
||||
TokenType: "Bearer",
|
||||
Deadline: time.Now().Add(1 * time.Minute),
|
||||
}
|
||||
|
||||
mux := http.NewServeMux()
|
||||
server := httptest.NewServer(mux)
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
mux.HandleFunc(pattern, handler)
|
||||
|
||||
client := NewClient("user", "secret")
|
||||
client.HTTPClient = server.Client()
|
||||
client.APIEndpoint, _ = url.Parse(server.URL)
|
||||
client.token = &Token{
|
||||
AccessToken: "secret",
|
||||
ExpiresIn: 60,
|
||||
TokenType: "Bearer",
|
||||
Deadline: time.Now().Add(1 * time.Minute),
|
||||
}
|
||||
|
||||
return client
|
||||
}
|
||||
|
||||
func writeFixtureHandler(method, filename string) http.HandlerFunc {
|
||||
return func(rw http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != method {
|
||||
http.Error(rw, fmt.Sprintf("unsupported method %s", req.Method), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
file, err := os.Open(filepath.Join("fixtures", filename))
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer func() { _ = file.Close() }()
|
||||
|
||||
_, _ = io.Copy(rw, file)
|
||||
}
|
||||
return client, nil
|
||||
},
|
||||
servermock.CheckHeader().WithJSONHeaders().
|
||||
WithAuthorization("Bearer xxx"))
|
||||
}
|
||||
|
||||
func TestClient_GetZones(t *testing.T) {
|
||||
client := setupTest(t, "/zones", writeFixtureHandler(http.MethodGet, "zones.json"))
|
||||
client := mockBuilder().
|
||||
Route("GET /zones",
|
||||
servermock.ResponseFromFixture("zones.json")).
|
||||
Build(t)
|
||||
|
||||
ctx := mockContext(t)
|
||||
|
||||
|
|
@ -78,7 +56,10 @@ func TestClient_GetZones(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_GetRecords(t *testing.T) {
|
||||
client := setupTest(t, "/zones/zzz/records", writeFixtureHandler(http.MethodGet, "records.json"))
|
||||
client := mockBuilder().
|
||||
Route("GET /zones/zzz/records",
|
||||
servermock.ResponseFromFixture("records.json")).
|
||||
Build(t)
|
||||
|
||||
ctx := mockContext(t)
|
||||
|
||||
|
|
@ -122,7 +103,11 @@ func TestClient_GetRecords(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_CreateRecord(t *testing.T) {
|
||||
client := setupTest(t, "/zones/zzz/records", writeFixtureHandler(http.MethodPost, "record.json"))
|
||||
client := mockBuilder().
|
||||
Route("POST /zones/zzz/records",
|
||||
servermock.ResponseFromFixture("record.json"),
|
||||
servermock.CheckRequestJSONBody(`{"name":"www.example.com.","type":"TXT","values":["text"],"ttl":"3600"}`)).
|
||||
Build(t)
|
||||
|
||||
ctx := mockContext(t)
|
||||
|
||||
|
|
@ -150,7 +135,10 @@ func TestClient_CreateRecord(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_DeleteRecord(t *testing.T) {
|
||||
client := setupTest(t, "/zones/zzz/records/example.com/TXT", writeFixtureHandler(http.MethodDelete, "record.json"))
|
||||
client := mockBuilder().
|
||||
Route("DELETE /zones/zzz/records/example.com/TXT",
|
||||
servermock.ResponseFromFixture("record.json")).
|
||||
Build(t)
|
||||
|
||||
ctx := mockContext(t)
|
||||
|
||||
|
|
|
|||
|
|
@ -2,13 +2,11 @@ package internal
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/go-acme/lego/v4/platform/tester/servermock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
|
@ -19,47 +17,33 @@ func mockContext(t *testing.T) context.Context {
|
|||
return context.WithValue(t.Context(), tokenKey, &Token{AccessToken: "xxx"})
|
||||
}
|
||||
|
||||
func tokenHandler(rw http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != http.MethodPost {
|
||||
http.Error(rw, fmt.Sprintf("invalid method, got %s want %s", req.Method, http.MethodPost), http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
err := req.ParseForm()
|
||||
if err != nil {
|
||||
http.Error(rw, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
grantType := req.Form.Get("grant_type")
|
||||
clientID := req.Form.Get("client_id")
|
||||
clientSecret := req.Form.Get("client_secret")
|
||||
|
||||
if clientID != "user" || clientSecret != "secret" || grantType != "access_key" {
|
||||
http.Error(rw, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
_ = json.NewEncoder(rw).Encode(Token{
|
||||
AccessToken: "xxx",
|
||||
TokenID: "yyy",
|
||||
ExpiresIn: 666,
|
||||
TokenType: "Bearer",
|
||||
Scope: "openid profile email roles",
|
||||
})
|
||||
}
|
||||
|
||||
func TestClient_obtainToken(t *testing.T) {
|
||||
mux := http.NewServeMux()
|
||||
server := httptest.NewServer(mux)
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
mux.HandleFunc("/", tokenHandler)
|
||||
|
||||
func setupIdentityClient(server *httptest.Server) (*Client, error) {
|
||||
client := NewClient("user", "secret")
|
||||
client.HTTPClient = server.Client()
|
||||
client.AuthEndpoint, _ = url.Parse(server.URL)
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func TestClient_obtainToken(t *testing.T) {
|
||||
client := servermock.NewBuilder[*Client](setupIdentityClient,
|
||||
servermock.CheckHeader().
|
||||
WithContentTypeFromURLEncoded(),
|
||||
).
|
||||
Route("POST /", servermock.JSONEncode(Token{
|
||||
AccessToken: "xxx",
|
||||
TokenID: "yyy",
|
||||
ExpiresIn: 666,
|
||||
TokenType: "Bearer",
|
||||
Scope: "openid profile email roles",
|
||||
}),
|
||||
servermock.CheckForm().Strict().
|
||||
With("client_id", "user").
|
||||
With("client_secret", "secret").
|
||||
With("grant_type", "access_key"),
|
||||
).
|
||||
Build(t)
|
||||
|
||||
assert.Nil(t, client.token)
|
||||
|
||||
tok, err := client.obtainToken(t.Context())
|
||||
|
|
@ -71,15 +55,23 @@ func TestClient_obtainToken(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_CreateAuthenticatedContext(t *testing.T) {
|
||||
mux := http.NewServeMux()
|
||||
server := httptest.NewServer(mux)
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
mux.HandleFunc("/", tokenHandler)
|
||||
|
||||
client := NewClient("user", "secret")
|
||||
client.HTTPClient = server.Client()
|
||||
client.AuthEndpoint, _ = url.Parse(server.URL)
|
||||
client := servermock.NewBuilder[*Client](setupIdentityClient,
|
||||
servermock.CheckHeader().
|
||||
WithContentTypeFromURLEncoded(),
|
||||
).
|
||||
Route("POST /", servermock.JSONEncode(Token{
|
||||
AccessToken: "xxx",
|
||||
TokenID: "yyy",
|
||||
ExpiresIn: 666,
|
||||
TokenType: "Bearer",
|
||||
Scope: "openid profile email roles",
|
||||
}),
|
||||
servermock.CheckForm().Strict().
|
||||
With("client_id", "user").
|
||||
With("client_secret", "secret").
|
||||
With("grant_type", "access_key"),
|
||||
).
|
||||
Build(t)
|
||||
|
||||
assert.Nil(t, client.token)
|
||||
|
||||
|
|
|
|||
|
|
@ -11,60 +11,26 @@ import (
|
|||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/go-acme/lego/v4/platform/tester/servermock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func setupTest(t *testing.T) (*Client, *http.ServeMux) {
|
||||
t.Helper()
|
||||
func mockBuilder() *servermock.Builder[*Client] {
|
||||
return servermock.NewBuilder[*Client](
|
||||
func(server *httptest.Server) (*Client, error) {
|
||||
client, err := NewClient("tyo1", "secret")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mux := http.NewServeMux()
|
||||
server := httptest.NewServer(mux)
|
||||
t.Cleanup(server.Close)
|
||||
client.HTTPClient = server.Client()
|
||||
client.baseURL, _ = url.Parse(server.URL)
|
||||
|
||||
client, err := NewClient("tyo1", "secret")
|
||||
require.NoError(t, err)
|
||||
|
||||
client.HTTPClient = server.Client()
|
||||
client.baseURL, _ = url.Parse(server.URL)
|
||||
|
||||
return client, mux
|
||||
}
|
||||
|
||||
func writeFixtureHandler(method, filename string) http.HandlerFunc {
|
||||
return func(rw http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != method {
|
||||
http.Error(rw, fmt.Sprintf("unsupported method %s", req.Method), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
writeFixture(rw, filename)
|
||||
}
|
||||
}
|
||||
|
||||
func writeBodyHandler(method, content string) http.HandlerFunc {
|
||||
return func(rw http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != method {
|
||||
http.Error(rw, fmt.Sprintf("unsupported method %s", req.Method), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
_, err := fmt.Fprint(rw, content)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func writeFixture(rw http.ResponseWriter, filename string) {
|
||||
file, err := os.Open(filepath.Join("fixtures", filename))
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer func() { _ = file.Close() }()
|
||||
|
||||
_, _ = io.Copy(rw, file)
|
||||
return client, nil
|
||||
},
|
||||
servermock.CheckHeader().WithJSONHeaders().
|
||||
With("X-Auth-Token", "secret"))
|
||||
}
|
||||
|
||||
func TestClient_GetDomainID(t *testing.T) {
|
||||
|
|
@ -76,34 +42,34 @@ func TestClient_GetDomainID(t *testing.T) {
|
|||
testCases := []struct {
|
||||
desc string
|
||||
domainName string
|
||||
handler http.HandlerFunc
|
||||
response string
|
||||
expected expected
|
||||
}{
|
||||
{
|
||||
desc: "success",
|
||||
domainName: "domain1.com.",
|
||||
handler: writeFixtureHandler(http.MethodGet, "domains_GET.json"),
|
||||
response: "domains_GET.json",
|
||||
expected: expected{domainID: "09494b72-b65b-4297-9efb-187f65a0553e"},
|
||||
},
|
||||
{
|
||||
desc: "non existing domain",
|
||||
domainName: "domain1.com.",
|
||||
handler: writeBodyHandler(http.MethodGet, "{}"),
|
||||
response: "empty.json",
|
||||
expected: expected{error: true},
|
||||
},
|
||||
{
|
||||
desc: "marshaling error",
|
||||
domainName: "domain1.com.",
|
||||
handler: writeBodyHandler(http.MethodGet, "[]"),
|
||||
response: "empty.json",
|
||||
expected: expected{error: true},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
client, mux := setupTest(t)
|
||||
|
||||
mux.Handle("/v1/domains", test.handler)
|
||||
client := mockBuilder().
|
||||
Route("GET /v1/domains", servermock.ResponseFromFixture(test.response)).
|
||||
Build(t)
|
||||
|
||||
domainID, err := client.GetDomainID(t.Context(), test.domainName)
|
||||
|
||||
|
|
@ -126,11 +92,6 @@ func TestClient_CreateRecord(t *testing.T) {
|
|||
{
|
||||
desc: "success",
|
||||
handler: func(rw http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != http.MethodPost {
|
||||
http.Error(rw, fmt.Sprintf("unsupported method %s", req.Method), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
raw, err := io.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusBadRequest)
|
||||
|
|
@ -143,18 +104,20 @@ func TestClient_CreateRecord(t *testing.T) {
|
|||
return
|
||||
}
|
||||
|
||||
writeFixture(rw, "domains-records_POST.json")
|
||||
file, err := os.Open(filepath.Join("fixtures", "domains-records_POST.json"))
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer func() { _ = file.Close() }()
|
||||
|
||||
_, _ = io.Copy(rw, file)
|
||||
},
|
||||
assert: require.NoError,
|
||||
},
|
||||
{
|
||||
desc: "bad request",
|
||||
handler: func(rw http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != http.MethodPost {
|
||||
http.Error(rw, fmt.Sprintf("unsupported method %s", req.Method), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
http.Error(rw, "OOPS", http.StatusBadRequest)
|
||||
},
|
||||
assert: require.Error,
|
||||
|
|
@ -163,9 +126,9 @@ func TestClient_CreateRecord(t *testing.T) {
|
|||
|
||||
for _, test := range testCases {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
client, mux := setupTest(t)
|
||||
|
||||
mux.Handle("/v1/domains/lego/records", test.handler)
|
||||
client := mockBuilder().
|
||||
Route("POST /v1/domains/lego/records", test.handler).
|
||||
Build(t)
|
||||
|
||||
domainID := "lego"
|
||||
|
||||
|
|
@ -183,10 +146,10 @@ func TestClient_CreateRecord(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_GetRecordID(t *testing.T) {
|
||||
client, mux := setupTest(t)
|
||||
|
||||
mux.HandleFunc("/v1/domains/89acac79-38e7-497d-807c-a011e1310438/records",
|
||||
writeFixtureHandler(http.MethodGet, "domains-records_GET.json"))
|
||||
client := mockBuilder().
|
||||
Route("GET /v1/domains/89acac79-38e7-497d-807c-a011e1310438/records",
|
||||
servermock.ResponseFromFixture("domains-records_GET.json")).
|
||||
Build(t)
|
||||
|
||||
recordID, err := client.GetRecordID(t.Context(), "89acac79-38e7-497d-807c-a011e1310438", "www.example.com.", "A", "15.185.172.153")
|
||||
require.NoError(t, err)
|
||||
|
|
@ -195,16 +158,10 @@ func TestClient_GetRecordID(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_DeleteRecord(t *testing.T) {
|
||||
client, mux := setupTest(t)
|
||||
|
||||
mux.HandleFunc("/v1/domains/89acac79-38e7-497d-807c-a011e1310438/records/2e32e609-3a4f-45ba-bdef-e50eacd345ad", func(rw http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != http.MethodDelete {
|
||||
http.Error(rw, fmt.Sprintf("unsupported method %s", req.Method), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
})
|
||||
client := mockBuilder().
|
||||
Route("DELETE /v1/domains/89acac79-38e7-497d-807c-a011e1310438/records/2e32e609-3a4f-45ba-bdef-e50eacd345ad",
|
||||
servermock.ResponseFromFixture("domains-records_GET.json")).
|
||||
Build(t)
|
||||
|
||||
err := client.DeleteRecord(t.Context(), "89acac79-38e7-497d-807c-a011e1310438", "2e32e609-3a4f-45ba-bdef-e50eacd345ad")
|
||||
require.NoError(t, err)
|
||||
|
|
|
|||
1
providers/dns/conoha/internal/fixtures/empty.json
Normal file
1
providers/dns/conoha/internal/fixtures/empty.json
Normal file
|
|
@ -0,0 +1 @@
|
|||
{}
|
||||
|
|
@ -1,27 +1,33 @@
|
|||
package internal
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/go-acme/lego/v4/platform/tester/servermock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestNewClient(t *testing.T) {
|
||||
mux := http.NewServeMux()
|
||||
server := httptest.NewServer(mux)
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
func setupIdentifier(server *httptest.Server) (*Identifier, error) {
|
||||
identifier, err := NewIdentifier("tyo1")
|
||||
require.NoError(t, err)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
identifier.HTTPClient = server.Client()
|
||||
identifier.baseURL, _ = url.Parse(server.URL)
|
||||
|
||||
mux.HandleFunc("/v2.0/tokens", writeFixtureHandler(http.MethodPost, "tokens_POST.json"))
|
||||
return identifier, nil
|
||||
}
|
||||
|
||||
func TestNewClient(t *testing.T) {
|
||||
identifier := servermock.NewBuilder[*Identifier](setupIdentifier,
|
||||
servermock.CheckHeader().WithJSONHeaders(),
|
||||
).
|
||||
Route("POST /v2.0/tokens", servermock.ResponseFromFixture("tokens_POST.json")).
|
||||
Build(t)
|
||||
|
||||
auth := Auth{
|
||||
TenantID: "487727e3921d44e3bfe7ebb337bf085e",
|
||||
|
|
|
|||
|
|
@ -11,60 +11,27 @@ import (
|
|||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/go-acme/lego/v4/platform/tester/servermock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func setupTest(t *testing.T) (*Client, *http.ServeMux) {
|
||||
t.Helper()
|
||||
func mockBuilder() *servermock.Builder[*Client] {
|
||||
return servermock.NewBuilder[*Client](
|
||||
func(server *httptest.Server) (*Client, error) {
|
||||
client, err := NewClient("c3j1", "secret")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mux := http.NewServeMux()
|
||||
server := httptest.NewServer(mux)
|
||||
t.Cleanup(server.Close)
|
||||
client.HTTPClient = server.Client()
|
||||
client.baseURL, _ = url.Parse(server.URL)
|
||||
|
||||
client, err := NewClient("c3j1", "secret")
|
||||
require.NoError(t, err)
|
||||
|
||||
client.HTTPClient = server.Client()
|
||||
client.baseURL, _ = url.Parse(server.URL)
|
||||
|
||||
return client, mux
|
||||
}
|
||||
|
||||
func writeFixtureHandler(method, filename string) http.HandlerFunc {
|
||||
return func(rw http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != method {
|
||||
http.Error(rw, fmt.Sprintf("unsupported method %s", req.Method), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
writeFixture(rw, filename)
|
||||
}
|
||||
}
|
||||
|
||||
func writeBodyHandler(method, content string) http.HandlerFunc {
|
||||
return func(rw http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != method {
|
||||
http.Error(rw, fmt.Sprintf("unsupported method %s", req.Method), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
_, err := fmt.Fprint(rw, content)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func writeFixture(rw http.ResponseWriter, filename string) {
|
||||
file, err := os.Open(filepath.Join("fixtures", filename))
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer func() { _ = file.Close() }()
|
||||
|
||||
_, _ = io.Copy(rw, file)
|
||||
return client, nil
|
||||
},
|
||||
servermock.CheckHeader().
|
||||
WithJSONHeaders().
|
||||
With("X-Auth-Token", "secret"))
|
||||
}
|
||||
|
||||
func TestClient_GetDomainID(t *testing.T) {
|
||||
|
|
@ -76,34 +43,34 @@ func TestClient_GetDomainID(t *testing.T) {
|
|||
testCases := []struct {
|
||||
desc string
|
||||
domainName string
|
||||
handler http.HandlerFunc
|
||||
response string
|
||||
expected expected
|
||||
}{
|
||||
{
|
||||
desc: "success",
|
||||
domainName: "domain1.com.",
|
||||
handler: writeFixtureHandler(http.MethodGet, "domains_GET.json"),
|
||||
response: "domains_GET.json",
|
||||
expected: expected{domainID: "09494b72-b65b-4297-9efb-187f65a0553e"},
|
||||
},
|
||||
{
|
||||
desc: "non existing domain",
|
||||
domainName: "domain1.com.",
|
||||
handler: writeBodyHandler(http.MethodGet, "{}"),
|
||||
response: "empty.json",
|
||||
expected: expected{error: true},
|
||||
},
|
||||
{
|
||||
desc: "marshaling error",
|
||||
domainName: "domain1.com.",
|
||||
handler: writeBodyHandler(http.MethodGet, "[]"),
|
||||
response: "empty.json",
|
||||
expected: expected{error: true},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
client, mux := setupTest(t)
|
||||
|
||||
mux.Handle("/v1/domains", test.handler)
|
||||
client := mockBuilder().
|
||||
Route("GET /v1/domains", servermock.ResponseFromFixture(test.response)).
|
||||
Build(t)
|
||||
|
||||
domainID, err := client.GetDomainID(t.Context(), test.domainName)
|
||||
|
||||
|
|
@ -126,11 +93,6 @@ func TestClient_CreateRecord(t *testing.T) {
|
|||
{
|
||||
desc: "success",
|
||||
handler: func(rw http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != http.MethodPost {
|
||||
http.Error(rw, fmt.Sprintf("unsupported method %s", req.Method), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
raw, err := io.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusBadRequest)
|
||||
|
|
@ -143,18 +105,20 @@ func TestClient_CreateRecord(t *testing.T) {
|
|||
return
|
||||
}
|
||||
|
||||
writeFixture(rw, "domains-records_POST.json")
|
||||
file, err := os.Open(filepath.Join("fixtures", "domains-records_POST.json"))
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer func() { _ = file.Close() }()
|
||||
|
||||
_, _ = io.Copy(rw, file)
|
||||
},
|
||||
assert: require.NoError,
|
||||
},
|
||||
{
|
||||
desc: "bad request",
|
||||
handler: func(rw http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != http.MethodPost {
|
||||
http.Error(rw, fmt.Sprintf("unsupported method %s", req.Method), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
http.Error(rw, "OOPS", http.StatusBadRequest)
|
||||
},
|
||||
assert: require.Error,
|
||||
|
|
@ -163,9 +127,9 @@ func TestClient_CreateRecord(t *testing.T) {
|
|||
|
||||
for _, test := range testCases {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
client, mux := setupTest(t)
|
||||
|
||||
mux.Handle("/v1/domains/lego/records", test.handler)
|
||||
client := mockBuilder().
|
||||
Route("POST /v1/domains/lego/records", test.handler).
|
||||
Build(t)
|
||||
|
||||
domainID := "lego"
|
||||
|
||||
|
|
@ -183,10 +147,10 @@ func TestClient_CreateRecord(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_GetRecordID(t *testing.T) {
|
||||
client, mux := setupTest(t)
|
||||
|
||||
mux.HandleFunc("/v1/domains/89acac79-38e7-497d-807c-a011e1310438/records",
|
||||
writeFixtureHandler(http.MethodGet, "domains-records_GET.json"))
|
||||
client := mockBuilder().
|
||||
Route("GET /v1/domains/89acac79-38e7-497d-807c-a011e1310438/records",
|
||||
servermock.ResponseFromFixture("domains-records_GET.json")).
|
||||
Build(t)
|
||||
|
||||
recordID, err := client.GetRecordID(t.Context(), "89acac79-38e7-497d-807c-a011e1310438", "www.example.com.", "A", "15.185.172.153")
|
||||
require.NoError(t, err)
|
||||
|
|
@ -195,16 +159,10 @@ func TestClient_GetRecordID(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_DeleteRecord(t *testing.T) {
|
||||
client, mux := setupTest(t)
|
||||
|
||||
mux.HandleFunc("/v1/domains/89acac79-38e7-497d-807c-a011e1310438/records/2e32e609-3a4f-45ba-bdef-e50eacd345ad", func(rw http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != http.MethodDelete {
|
||||
http.Error(rw, fmt.Sprintf("unsupported method %s", req.Method), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
})
|
||||
client := mockBuilder().
|
||||
Route("DELETE /v1/domains/89acac79-38e7-497d-807c-a011e1310438/records/2e32e609-3a4f-45ba-bdef-e50eacd345ad",
|
||||
servermock.ResponseFromFixture("domains-records_GET.json")).
|
||||
Build(t)
|
||||
|
||||
err := client.DeleteRecord(t.Context(), "89acac79-38e7-497d-807c-a011e1310438", "2e32e609-3a4f-45ba-bdef-e50eacd345ad")
|
||||
require.NoError(t, err)
|
||||
|
|
|
|||
1
providers/dns/conohav3/internal/fixtures/empty.json
Normal file
1
providers/dns/conohav3/internal/fixtures/empty.json
Normal file
|
|
@ -0,0 +1 @@
|
|||
{}
|
||||
|
|
@ -6,26 +6,32 @@ import (
|
|||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/go-acme/lego/v4/platform/tester/servermock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestGetToken_HeaderToken(t *testing.T) {
|
||||
mux := http.NewServeMux()
|
||||
server := httptest.NewServer(mux)
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
func setupIdentifier(server *httptest.Server) (*Identifier, error) {
|
||||
identifier, err := NewIdentifier("c3j1")
|
||||
require.NoError(t, err)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
identifier.HTTPClient = server.Client()
|
||||
identifier.baseURL, _ = url.Parse(server.URL)
|
||||
|
||||
mux.HandleFunc("/v3/auth/tokens", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("x-subject-token", "sample-header-token-123")
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
_, _ = w.Write([]byte(`{}`))
|
||||
})
|
||||
return identifier, nil
|
||||
}
|
||||
|
||||
func TestGetToken_HeaderToken(t *testing.T) {
|
||||
identifier := servermock.NewBuilder[*Identifier](setupIdentifier,
|
||||
servermock.CheckHeader().WithJSONHeaders(),
|
||||
).
|
||||
Route("POST /v3/auth/tokens",
|
||||
servermock.ResponseFromFixture("empty.json").
|
||||
WithStatusCode(http.StatusCreated).
|
||||
WithHeader("x-subject-token", "sample-header-token-123")).
|
||||
Build(t)
|
||||
|
||||
auth := Auth{
|
||||
Identity: Identity{
|
||||
|
|
|
|||
|
|
@ -1,51 +1,30 @@
|
|||
package internal
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/go-acme/lego/v4/platform/tester/servermock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func setupTest(t *testing.T) (*Client, *http.ServeMux) {
|
||||
t.Helper()
|
||||
func mockBuilder() *servermock.Builder[*Client] {
|
||||
return servermock.NewBuilder[*Client](
|
||||
func(server *httptest.Server) (*Client, error) {
|
||||
client := NewClient(server.Client())
|
||||
client.BaseURL = server.URL
|
||||
|
||||
mux := http.NewServeMux()
|
||||
server := httptest.NewServer(mux)
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
client := NewClient(server.Client())
|
||||
client.BaseURL = server.URL
|
||||
|
||||
return client, mux
|
||||
return client, nil
|
||||
},
|
||||
servermock.CheckHeader().WithJSONHeaders(),
|
||||
)
|
||||
}
|
||||
|
||||
func TestDomainService_GetAll(t *testing.T) {
|
||||
client, mux := setupTest(t)
|
||||
|
||||
mux.HandleFunc("/v1/domains", func(rw http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != http.MethodGet {
|
||||
http.Error(rw, "invalid method: "+req.Method, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
file, err := os.Open("./fixtures/domains-GetAll.json")
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer func() { _ = file.Close() }()
|
||||
|
||||
_, err = io.Copy(rw, file)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
})
|
||||
client := mockBuilder().
|
||||
Route("GET /v1/domains", servermock.ResponseFromFixture("domains-GetAll.json")).
|
||||
Build(t)
|
||||
|
||||
data, err := client.Domains.GetAll(t.Context(), nil)
|
||||
require.NoError(t, err)
|
||||
|
|
@ -61,27 +40,12 @@ func TestDomainService_GetAll(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestDomainService_Search(t *testing.T) {
|
||||
client, mux := setupTest(t)
|
||||
|
||||
mux.HandleFunc("/v1/domains/search", func(rw http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != http.MethodGet {
|
||||
http.Error(rw, "invalid method: "+req.Method, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
file, err := os.Open("./fixtures/domains-Search.json")
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer func() { _ = file.Close() }()
|
||||
|
||||
_, err = io.Copy(rw, file)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
})
|
||||
client := mockBuilder().
|
||||
Route("GET /v1/domains/search",
|
||||
servermock.ResponseFromFixture("domains-Search.json"),
|
||||
servermock.CheckQueryParameter().Strict().
|
||||
With("exact", "lego.wtf")).
|
||||
Build(t)
|
||||
|
||||
data, err := client.Domains.Search(t.Context(), Exact, "lego.wtf")
|
||||
require.NoError(t, err)
|
||||
|
|
|
|||
|
|
@ -2,37 +2,19 @@ package internal
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/go-acme/lego/v4/platform/tester/servermock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestTxtRecordService_Create(t *testing.T) {
|
||||
client, mux := setupTest(t)
|
||||
|
||||
mux.HandleFunc("/v1/domains/12345/records/txt", func(rw http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != http.MethodPost {
|
||||
http.Error(rw, "invalid method: "+req.Method, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
file, err := os.Open("./fixtures/records-Create.json")
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer func() { _ = file.Close() }()
|
||||
|
||||
_, err = io.Copy(rw, file)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
})
|
||||
client := mockBuilder().
|
||||
Route("POST /v1/domains/12345/records/txt", servermock.ResponseFromFixture("records-Create.json"),
|
||||
servermock.CheckRequestJSONBody(`{"name":""}`)).
|
||||
Build(t)
|
||||
|
||||
records, err := client.TxtRecords.Create(t.Context(), 12345, RecordRequest{})
|
||||
require.NoError(t, err)
|
||||
|
|
@ -47,27 +29,9 @@ func TestTxtRecordService_Create(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestTxtRecordService_GetAll(t *testing.T) {
|
||||
client, mux := setupTest(t)
|
||||
|
||||
mux.HandleFunc("/v1/domains/12345/records/txt", func(rw http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != http.MethodGet {
|
||||
http.Error(rw, "invalid method: "+req.Method, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
file, err := os.Open("./fixtures/records-GetAll.json")
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer func() { _ = file.Close() }()
|
||||
|
||||
_, err = io.Copy(rw, file)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
})
|
||||
client := mockBuilder().
|
||||
Route("GET /v1/domains/12345/records/txt", servermock.ResponseFromFixture("records-GetAll.json")).
|
||||
Build(t)
|
||||
|
||||
records, err := client.TxtRecords.GetAll(t.Context(), 12345)
|
||||
require.NoError(t, err)
|
||||
|
|
@ -82,27 +46,9 @@ func TestTxtRecordService_GetAll(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestTxtRecordService_Get(t *testing.T) {
|
||||
client, mux := setupTest(t)
|
||||
|
||||
mux.HandleFunc("/v1/domains/12345/records/txt/6789", func(rw http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != http.MethodGet {
|
||||
http.Error(rw, "invalid method: "+req.Method, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
file, err := os.Open("./fixtures/records-Get.json")
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer func() { _ = file.Close() }()
|
||||
|
||||
_, err = io.Copy(rw, file)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
})
|
||||
client := mockBuilder().
|
||||
Route("GET /v1/domains/12345/records/txt/6789", servermock.ResponseFromFixture("records-Get.json")).
|
||||
Build(t)
|
||||
|
||||
record, err := client.TxtRecords.Get(t.Context(), 12345, 6789)
|
||||
require.NoError(t, err)
|
||||
|
|
@ -130,20 +76,10 @@ func TestTxtRecordService_Get(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestTxtRecordService_Update(t *testing.T) {
|
||||
client, mux := setupTest(t)
|
||||
|
||||
mux.HandleFunc("/v1/domains/12345/records/txt/6789", func(rw http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != http.MethodPut {
|
||||
http.Error(rw, "invalid method: "+req.Method, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
_, err := rw.Write([]byte(`{"success":"Record updated successfully"}`))
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
})
|
||||
client := mockBuilder().
|
||||
Route("PUT /v1/domains/12345/records/txt/6789",
|
||||
servermock.RawStringResponse(`{"success":"Record updated successfully"}`)).
|
||||
Build(t)
|
||||
|
||||
msg, err := client.TxtRecords.Update(t.Context(), 12345, 6789, RecordRequest{})
|
||||
require.NoError(t, err)
|
||||
|
|
@ -153,20 +89,10 @@ func TestTxtRecordService_Update(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestTxtRecordService_Delete(t *testing.T) {
|
||||
client, mux := setupTest(t)
|
||||
|
||||
mux.HandleFunc("/v1/domains/12345/records/txt/6789", func(rw http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != http.MethodDelete {
|
||||
http.Error(rw, "invalid method: "+req.Method, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
_, err := rw.Write([]byte(`{"success":"Record deleted successfully"}`))
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
})
|
||||
client := mockBuilder().
|
||||
Route("DELETE /v1/domains/12345/records/txt/6789",
|
||||
servermock.RawStringResponse(`{"success":"Record deleted successfully"}`)).
|
||||
Build(t)
|
||||
|
||||
msg, err := client.TxtRecords.Delete(t.Context(), 12345, 6789)
|
||||
require.NoError(t, err)
|
||||
|
|
@ -176,27 +102,9 @@ func TestTxtRecordService_Delete(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestTxtRecordService_Search(t *testing.T) {
|
||||
client, mux := setupTest(t)
|
||||
|
||||
mux.HandleFunc("/v1/domains/12345/records/txt/search", func(rw http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != http.MethodGet {
|
||||
http.Error(rw, "invalid method: "+req.Method, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
file, err := os.Open("./fixtures/records-Search.json")
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer func() { _ = file.Close() }()
|
||||
|
||||
_, err = io.Copy(rw, file)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
})
|
||||
client := mockBuilder().
|
||||
Route("GET /v1/domains/12345/records/txt/search", servermock.ResponseFromFixture("records-Search.json")).
|
||||
Build(t)
|
||||
|
||||
records, err := client.TxtRecords.Search(t.Context(), 12345, Exact, "test")
|
||||
require.NoError(t, err)
|
||||
|
|
|
|||
|
|
@ -1,112 +1,34 @@
|
|||
package internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/go-acme/lego/v4/platform/tester/servermock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func setupTest(t *testing.T) (*Client, *http.ServeMux) {
|
||||
t.Helper()
|
||||
func mockBuilder() *servermock.Builder[*Client] {
|
||||
return servermock.NewBuilder[*Client](
|
||||
func(server *httptest.Server) (*Client, error) {
|
||||
client := NewClient("user", "secret")
|
||||
client.baseURL, _ = url.Parse(server.URL)
|
||||
client.HTTPClient = server.Client()
|
||||
|
||||
mux := http.NewServeMux()
|
||||
server := httptest.NewServer(mux)
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
client := NewClient("user", "secret")
|
||||
client.baseURL, _ = url.Parse(server.URL)
|
||||
client.HTTPClient = server.Client()
|
||||
|
||||
return client, mux
|
||||
}
|
||||
|
||||
func testHandler(method string, statusCode int, filename string) http.HandlerFunc {
|
||||
return func(rw http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != method {
|
||||
http.Error(rw, fmt.Sprintf(`unsupported method: %s`, req.Method), http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
rw.WriteHeader(statusCode)
|
||||
|
||||
if statusCode == http.StatusNoContent {
|
||||
return
|
||||
}
|
||||
|
||||
file, err := os.Open(filepath.Join("fixtures", filename))
|
||||
if err != nil {
|
||||
http.Error(rw, fmt.Sprintf(`message %v`, err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
defer func() { _ = file.Close() }()
|
||||
|
||||
_, err = io.Copy(rw, file)
|
||||
if err != nil {
|
||||
http.Error(rw, fmt.Sprintf(`message %v`, err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testHandlerAuth(method string, statusCode int, filename string) http.HandlerFunc {
|
||||
return func(rw http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != method {
|
||||
http.Error(rw, fmt.Sprintf(`{"message":"unsupported method: %s"}`, req.Method), http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
rw.WriteHeader(statusCode)
|
||||
|
||||
if statusCode == http.StatusNoContent {
|
||||
return
|
||||
}
|
||||
|
||||
file, err := os.Open(filepath.Join("fixtures", filename))
|
||||
if err != nil {
|
||||
http.Error(rw, fmt.Sprintf(`{"message":"%v"}`, err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
defer func() { _ = file.Close() }()
|
||||
|
||||
_, err = io.Copy(rw, file)
|
||||
if err != nil {
|
||||
http.Error(rw, fmt.Sprintf(`{"message":"%v"}`, err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_CreateAuthenticationToken(t *testing.T) {
|
||||
client, mux := setupTest(t)
|
||||
|
||||
mux.HandleFunc("/auth/token", testHandlerAuth(http.MethodPost, http.StatusOK, "auth.json"))
|
||||
|
||||
ctx := t.Context()
|
||||
|
||||
token, err := client.CreateAuthenticationToken(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
expected := &Token{
|
||||
Token: "authsecret",
|
||||
Expires: 123,
|
||||
}
|
||||
assert.Equal(t, expected, token)
|
||||
return client, nil
|
||||
},
|
||||
servermock.CheckHeader().WithJSONHeaders(),
|
||||
)
|
||||
}
|
||||
|
||||
func TestClient_ListZone(t *testing.T) {
|
||||
client, mux := setupTest(t)
|
||||
|
||||
mux.HandleFunc("/dnszones/", testHandler(http.MethodGet, http.StatusOK, "ListZone.json"))
|
||||
client := mockBuilder().
|
||||
Route("GET /dnszones/",
|
||||
servermock.ResponseFromFixture("ListZone.json")).
|
||||
Build(t)
|
||||
|
||||
ctx := t.Context()
|
||||
|
||||
|
|
@ -122,13 +44,12 @@ func TestClient_ListZone(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_GetZoneDetails(t *testing.T) {
|
||||
client, mux := setupTest(t)
|
||||
client := mockBuilder().
|
||||
Route("GET /dnszones/example.com",
|
||||
servermock.ResponseFromFixture("GetZoneDetails.json")).
|
||||
Build(t)
|
||||
|
||||
mux.HandleFunc("/dnszones/example.com", testHandler(http.MethodGet, http.StatusOK, "GetZoneDetails.json"))
|
||||
|
||||
ctx := t.Context()
|
||||
|
||||
zone, err := client.GetZoneDetails(ctx, "example.com")
|
||||
zone, err := client.GetZoneDetails(t.Context(), "example.com")
|
||||
require.NoError(t, err)
|
||||
|
||||
expected := &ZoneDetails{
|
||||
|
|
@ -142,13 +63,12 @@ func TestClient_GetZoneDetails(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_ListRecords(t *testing.T) {
|
||||
client, mux := setupTest(t)
|
||||
client := mockBuilder().
|
||||
Route("GET /dnszones/example.com/records/",
|
||||
servermock.ResponseFromFixture("ListRecords.json")).
|
||||
Build(t)
|
||||
|
||||
mux.HandleFunc("/dnszones/example.com/records/", testHandler(http.MethodGet, http.StatusOK, "ListRecords.json"))
|
||||
|
||||
ctx := t.Context()
|
||||
|
||||
records, err := client.ListRecords(ctx, "example.com")
|
||||
records, err := client.ListRecords(t.Context(), "example.com")
|
||||
require.NoError(t, err)
|
||||
|
||||
expected := []Record{
|
||||
|
|
@ -176,38 +96,35 @@ func TestClient_ListRecords(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_AddRecord(t *testing.T) {
|
||||
client, mux := setupTest(t)
|
||||
|
||||
mux.HandleFunc("/dnszones/example.com/records/", testHandler(http.MethodPost, http.StatusNoContent, ""))
|
||||
|
||||
ctx := t.Context()
|
||||
client := mockBuilder().
|
||||
Route("POST /dnszones/example.com/records/",
|
||||
servermock.Noop().WithStatusCode(http.StatusNoContent)).
|
||||
Build(t)
|
||||
|
||||
record := Record{Name: "www", TTL: 3600, Type: "A", Data: "127.0.0.1"}
|
||||
|
||||
err := client.AddRecord(ctx, "example.com", record)
|
||||
err := client.AddRecord(t.Context(), "example.com", record)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestClient_DeleteRecords(t *testing.T) {
|
||||
client, mux := setupTest(t)
|
||||
|
||||
mux.HandleFunc("/dnszones/example.com/records/delete", testHandler(http.MethodPost, http.StatusNoContent, ""))
|
||||
|
||||
ctx := t.Context()
|
||||
client := mockBuilder().
|
||||
Route("POST /dnszones/example.com/records/delete",
|
||||
servermock.Noop().WithStatusCode(http.StatusNoContent)).
|
||||
Build(t)
|
||||
|
||||
record := Record{Name: "www", Type: "A", Data: "127.0.0.1"}
|
||||
|
||||
err := client.DeleteRecords(ctx, "example.com", record)
|
||||
err := client.DeleteRecords(t.Context(), "example.com", record)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestClient_CommitRecords(t *testing.T) {
|
||||
client, mux := setupTest(t)
|
||||
client := mockBuilder().
|
||||
Route("POST /dnszones/example.com/records/commit",
|
||||
servermock.Noop().WithStatusCode(http.StatusNoContent)).
|
||||
Build(t)
|
||||
|
||||
mux.HandleFunc("/dnszones/example.com/records/commit", testHandler(http.MethodPost, http.StatusNoContent, ""))
|
||||
|
||||
ctx := t.Context()
|
||||
|
||||
err := client.CommitRecords(ctx, "example.com")
|
||||
err := client.CommitRecords(t.Context(), "example.com")
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
|
|
|||
24
providers/dns/corenetworks/internal/identity_test.go
Normal file
24
providers/dns/corenetworks/internal/identity_test.go
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
package internal
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/go-acme/lego/v4/platform/tester/servermock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestClient_CreateAuthenticationToken(t *testing.T) {
|
||||
client := mockBuilder().
|
||||
Route("POST /auth/token", servermock.ResponseFromFixture("auth.json")).
|
||||
Build(t)
|
||||
|
||||
token, err := client.CreateAuthenticationToken(t.Context())
|
||||
require.NoError(t, err)
|
||||
|
||||
expected := &Token{
|
||||
Token: "authsecret",
|
||||
Expires: 123,
|
||||
}
|
||||
assert.Equal(t, expected, token)
|
||||
}
|
||||
|
|
@ -1,58 +1,38 @@
|
|||
package cpanel
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/go-acme/lego/v4/platform/tester/servermock"
|
||||
"github.com/go-acme/lego/v4/providers/dns/cpanel/internal/shared"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func setupTest(t *testing.T, pattern, filename string) *Client {
|
||||
t.Helper()
|
||||
func mockBuilder() *servermock.Builder[*Client] {
|
||||
return servermock.NewBuilder[*Client](
|
||||
func(server *httptest.Server) (*Client, error) {
|
||||
client, err := NewClient(server.URL, "user", "secret")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mux := http.NewServeMux()
|
||||
server := httptest.NewServer(mux)
|
||||
t.Cleanup(server.Close)
|
||||
client.HTTPClient = server.Client()
|
||||
|
||||
mux.HandleFunc(pattern, func(rw http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != http.MethodGet {
|
||||
http.Error(rw, fmt.Sprintf("unsupported method %s", req.Method), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
open, err := os.Open(filepath.Join("fixtures", filename))
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
defer func() { _ = open.Close() }()
|
||||
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
_, err = io.Copy(rw, open)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
client, err := NewClient(server.URL, "user", "secret")
|
||||
require.NoError(t, err)
|
||||
|
||||
client.HTTPClient = server.Client()
|
||||
|
||||
return client
|
||||
return client, nil
|
||||
},
|
||||
servermock.CheckHeader().WithJSONHeaders().
|
||||
WithAuthorization("cpanel user:secret"))
|
||||
}
|
||||
|
||||
func TestClient_FetchZoneInformation(t *testing.T) {
|
||||
client := setupTest(t, "/execute/DNS/parse_zone", "zone-info.json")
|
||||
client := mockBuilder().
|
||||
Route("GET /execute/DNS/parse_zone",
|
||||
servermock.ResponseFromFixture("zone-info.json"),
|
||||
servermock.CheckQueryParameter().Strict().
|
||||
With("zone", "example.com")).
|
||||
Build(t)
|
||||
|
||||
zoneInfo, err := client.FetchZoneInformation(t.Context(), "example.com")
|
||||
require.NoError(t, err)
|
||||
|
|
@ -70,16 +50,27 @@ func TestClient_FetchZoneInformation(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_FetchZoneInformation_error(t *testing.T) {
|
||||
client := setupTest(t, "/execute/DNS/parse_zone", "zone-info_error.json")
|
||||
client := mockBuilder().
|
||||
Route("GET /execute/DNS/parse_zone",
|
||||
servermock.ResponseFromFixture("zone-info_error.json")).
|
||||
Build(t)
|
||||
|
||||
zoneInfo, err := client.FetchZoneInformation(t.Context(), "example.com")
|
||||
require.Error(t, err)
|
||||
require.EqualError(t, err, "error(0): You do not control a DNS zone named example.com.: a, b, c")
|
||||
|
||||
assert.Nil(t, zoneInfo)
|
||||
}
|
||||
|
||||
func TestClient_AddRecord(t *testing.T) {
|
||||
client := setupTest(t, "/execute/DNS/mass_edit_zone", "update-zone.json")
|
||||
client := mockBuilder().
|
||||
Route("GET /execute/DNS/mass_edit_zone",
|
||||
servermock.ResponseFromFixture("update-zone.json"),
|
||||
servermock.CheckQueryParameter().Strict().
|
||||
With("zone", "example.com").
|
||||
With("add", `{"dname":"example","ttl":14400,"record_type":"TXT","data":["string1","string2"]}`).
|
||||
With("serial", "123456").
|
||||
With("zone", "example.com")).
|
||||
Build(t)
|
||||
|
||||
record := shared.Record{
|
||||
DName: "example",
|
||||
|
|
@ -97,7 +88,10 @@ func TestClient_AddRecord(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_AddRecord_error(t *testing.T) {
|
||||
client := setupTest(t, "/execute/DNS/mass_edit_zone", "update-zone_error.json")
|
||||
client := mockBuilder().
|
||||
Route("GET /execute/DNS/mass_edit_zone",
|
||||
servermock.ResponseFromFixture("update-zone_error.json")).
|
||||
Build(t)
|
||||
|
||||
record := shared.Record{
|
||||
DName: "example",
|
||||
|
|
@ -113,7 +107,14 @@ func TestClient_AddRecord_error(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_EditRecord(t *testing.T) {
|
||||
client := setupTest(t, "/execute/DNS/mass_edit_zone", "update-zone.json")
|
||||
client := mockBuilder().
|
||||
Route("GET /execute/DNS/mass_edit_zone",
|
||||
servermock.ResponseFromFixture("update-zone.json"),
|
||||
servermock.CheckQueryParameter().Strict().
|
||||
With("edit", `{"dname":"example","ttl":14400,"record_type":"TXT","data":["string1","string2"],"line_index":9}`).
|
||||
With("serial", "123456").
|
||||
With("zone", "example.com")).
|
||||
Build(t)
|
||||
|
||||
record := shared.Record{
|
||||
LineIndex: 9,
|
||||
|
|
@ -132,7 +133,10 @@ func TestClient_EditRecord(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_EditRecord_error(t *testing.T) {
|
||||
client := setupTest(t, "/execute/DNS/mass_edit_zone", "update-zone_error.json")
|
||||
client := mockBuilder().
|
||||
Route("GET /execute/DNS/mass_edit_zone",
|
||||
servermock.ResponseFromFixture("update-zone_error.json")).
|
||||
Build(t)
|
||||
|
||||
record := shared.Record{
|
||||
LineIndex: 9,
|
||||
|
|
@ -149,7 +153,14 @@ func TestClient_EditRecord_error(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_DeleteRecord(t *testing.T) {
|
||||
client := setupTest(t, "/execute/DNS/mass_edit_zone", "update-zone.json")
|
||||
client := mockBuilder().
|
||||
Route("GET /execute/DNS/mass_edit_zone",
|
||||
servermock.ResponseFromFixture("update-zone.json"),
|
||||
servermock.CheckQueryParameter().Strict().
|
||||
With("remove", "0").
|
||||
With("serial", "123456").
|
||||
With("zone", "example.com")).
|
||||
Build(t)
|
||||
|
||||
zoneSerial, err := client.DeleteRecord(t.Context(), 123456, "example.com", 0)
|
||||
require.NoError(t, err)
|
||||
|
|
@ -160,7 +171,10 @@ func TestClient_DeleteRecord(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_DeleteRecord_error(t *testing.T) {
|
||||
client := setupTest(t, "/execute/DNS/mass_edit_zone", "update-zone_error.json")
|
||||
client := mockBuilder().
|
||||
Route("GET /execute/DNS/mass_edit_zone",
|
||||
servermock.ResponseFromFixture("update-zone_error.json")).
|
||||
Build(t)
|
||||
|
||||
zoneSerial, err := client.DeleteRecord(t.Context(), 123456, "example.com", 0)
|
||||
require.Error(t, err)
|
||||
|
|
|
|||
|
|
@ -1,58 +1,39 @@
|
|||
package whm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/go-acme/lego/v4/platform/tester/servermock"
|
||||
"github.com/go-acme/lego/v4/providers/dns/cpanel/internal/shared"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func setupTest(t *testing.T, pattern, filename string) *Client {
|
||||
t.Helper()
|
||||
func mockBuilder() *servermock.Builder[*Client] {
|
||||
return servermock.NewBuilder[*Client](
|
||||
func(server *httptest.Server) (*Client, error) {
|
||||
client, err := NewClient(server.URL, "user", "secret")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mux := http.NewServeMux()
|
||||
server := httptest.NewServer(mux)
|
||||
t.Cleanup(server.Close)
|
||||
client.HTTPClient = server.Client()
|
||||
|
||||
mux.HandleFunc(pattern, func(rw http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != http.MethodGet {
|
||||
http.Error(rw, fmt.Sprintf("unsupported method %s", req.Method), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
open, err := os.Open(filepath.Join("fixtures", filename))
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
defer func() { _ = open.Close() }()
|
||||
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
_, err = io.Copy(rw, open)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
client, err := NewClient(server.URL, "user", "secret")
|
||||
require.NoError(t, err)
|
||||
|
||||
client.HTTPClient = server.Client()
|
||||
|
||||
return client
|
||||
return client, nil
|
||||
},
|
||||
servermock.CheckHeader().WithJSONHeaders().
|
||||
WithAuthorization("whm user:secret"))
|
||||
}
|
||||
|
||||
func TestClient_FetchZoneInformation(t *testing.T) {
|
||||
client := setupTest(t, "/json-api/parse_dns_zone", "zone-info.json")
|
||||
client := mockBuilder().
|
||||
Route("GET /json-api/parse_dns_zone",
|
||||
servermock.ResponseFromFixture("zone-info.json"),
|
||||
servermock.CheckQueryParameter().Strict().
|
||||
With("api.version", "1").
|
||||
With("zone", "example.com")).
|
||||
Build(t)
|
||||
|
||||
zoneInfo, err := client.FetchZoneInformation(t.Context(), "example.com")
|
||||
require.NoError(t, err)
|
||||
|
|
@ -70,7 +51,10 @@ func TestClient_FetchZoneInformation(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_FetchZoneInformation_error(t *testing.T) {
|
||||
client := setupTest(t, "/json-api/parse_dns_zone", "zone-info_error.json")
|
||||
client := mockBuilder().
|
||||
Route("GET /json-api/parse_dns_zone",
|
||||
servermock.ResponseFromFixture("zone-info_error.json")).
|
||||
Build(t)
|
||||
|
||||
zoneInfo, err := client.FetchZoneInformation(t.Context(), "example.com")
|
||||
require.Error(t, err)
|
||||
|
|
@ -79,7 +63,15 @@ func TestClient_FetchZoneInformation_error(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_AddRecord(t *testing.T) {
|
||||
client := setupTest(t, "/json-api/mass_edit_dns_zone", "update-zone.json")
|
||||
client := mockBuilder().
|
||||
Route("GET /json-api/mass_edit_dns_zone",
|
||||
servermock.ResponseFromFixture("update-zone.json"),
|
||||
servermock.CheckQueryParameter().Strict().
|
||||
With("add", `{"dname":"example","ttl":14400,"record_type":"TXT","data":["string1","string2"]}`).
|
||||
With("api.version", "1").
|
||||
With("serial", "123456").
|
||||
With("zone", "example.com")).
|
||||
Build(t)
|
||||
|
||||
record := shared.Record{
|
||||
DName: "example",
|
||||
|
|
@ -97,7 +89,10 @@ func TestClient_AddRecord(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_AddRecord_error(t *testing.T) {
|
||||
client := setupTest(t, "/json-api/mass_edit_dns_zone", "update-zone_error.json")
|
||||
client := mockBuilder().
|
||||
Route("GET /json-api/mass_edit_dns_zone",
|
||||
servermock.ResponseFromFixture("update-zone_error.json")).
|
||||
Build(t)
|
||||
|
||||
record := shared.Record{
|
||||
DName: "example",
|
||||
|
|
@ -113,7 +108,15 @@ func TestClient_AddRecord_error(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_EditRecord(t *testing.T) {
|
||||
client := setupTest(t, "/json-api/mass_edit_dns_zone", "update-zone.json")
|
||||
client := mockBuilder().
|
||||
Route("GET /json-api/mass_edit_dns_zone",
|
||||
servermock.ResponseFromFixture("update-zone.json"),
|
||||
servermock.CheckQueryParameter().Strict().
|
||||
With("edit", `{"dname":"example","ttl":14400,"record_type":"TXT","data":["string1","string2"],"line_index":9}`).
|
||||
With("api.version", "1").
|
||||
With("serial", "123456").
|
||||
With("zone", "example.com")).
|
||||
Build(t)
|
||||
|
||||
record := shared.Record{
|
||||
LineIndex: 9,
|
||||
|
|
@ -132,7 +135,10 @@ func TestClient_EditRecord(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_EditRecord_error(t *testing.T) {
|
||||
client := setupTest(t, "/json-api/mass_edit_dns_zone", "update-zone_error.json")
|
||||
client := mockBuilder().
|
||||
Route("GET /json-api/mass_edit_dns_zone",
|
||||
servermock.ResponseFromFixture("update-zone_error.json")).
|
||||
Build(t)
|
||||
|
||||
record := shared.Record{
|
||||
LineIndex: 9,
|
||||
|
|
@ -149,7 +155,15 @@ func TestClient_EditRecord_error(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_DeleteRecord(t *testing.T) {
|
||||
client := setupTest(t, "/json-api/mass_edit_dns_zone", "update-zone.json")
|
||||
client := mockBuilder().
|
||||
Route("GET /json-api/mass_edit_dns_zone",
|
||||
servermock.ResponseFromFixture("update-zone.json"),
|
||||
servermock.CheckQueryParameter().Strict().
|
||||
With("remove", "0").
|
||||
With("api.version", "1").
|
||||
With("serial", "123456").
|
||||
With("zone", "example.com")).
|
||||
Build(t)
|
||||
|
||||
zoneSerial, err := client.DeleteRecord(t.Context(), 123456, "example.com", 0)
|
||||
require.NoError(t, err)
|
||||
|
|
@ -160,7 +174,10 @@ func TestClient_DeleteRecord(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_DeleteRecord_error(t *testing.T) {
|
||||
client := setupTest(t, "/json-api/mass_edit_dns_zone", "update-zone_error.json")
|
||||
client := mockBuilder().
|
||||
Route("GET /json-api/mass_edit_dns_zone",
|
||||
servermock.ResponseFromFixture("update-zone_error.json")).
|
||||
Build(t)
|
||||
|
||||
zoneSerial, err := client.DeleteRecord(t.Context(), 123456, "example.com", 0)
|
||||
require.Error(t, err)
|
||||
|
|
|
|||
|
|
@ -1,80 +1,37 @@
|
|||
package internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/platform/tester/servermock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func setupTest(t *testing.T) (*Client, *http.ServeMux) {
|
||||
t.Helper()
|
||||
|
||||
mux := http.NewServeMux()
|
||||
server := httptest.NewServer(mux)
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
func setupClient(server *httptest.Server) (*Client, error) {
|
||||
client := NewClient("secret")
|
||||
client.baseURL, _ = url.Parse(server.URL)
|
||||
client.zoneEndpoint = server.URL
|
||||
client.HTTPClient = server.Client()
|
||||
|
||||
return client, mux
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func testHandler(method string, statusCode int, filename string) func(rw http.ResponseWriter, req *http.Request) {
|
||||
return func(rw http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != method {
|
||||
http.Error(rw, fmt.Sprintf("unsupported method %s", req.Method), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
username, password, ok := req.BasicAuth()
|
||||
if !ok {
|
||||
http.Error(rw, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
if username != "api" {
|
||||
http.Error(rw, fmt.Sprintf("username: want %s got %s", username, "user"), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
if password != "secret" {
|
||||
http.Error(rw, fmt.Sprintf("password: want %s got %s", password, "secret"), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
file, err := os.Open(filepath.Join("fixtures", filename))
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer func() { _ = file.Close() }()
|
||||
|
||||
rw.WriteHeader(statusCode)
|
||||
|
||||
_, err = io.Copy(rw, file)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
func mockBuilder() *servermock.Builder[*Client] {
|
||||
return servermock.NewBuilder[*Client](setupClient,
|
||||
servermock.CheckHeader().WithJSONHeaders().
|
||||
WithBasicAuth("api", "secret"))
|
||||
}
|
||||
|
||||
func TestGetRecords(t *testing.T) {
|
||||
client, mux := setupTest(t)
|
||||
|
||||
mux.HandleFunc("/zones/47c0ecf6c91243308c649ad1d2d618dd/dnsrecords",
|
||||
testHandler(http.MethodGet, http.StatusOK, "records-GET.json"))
|
||||
client := mockBuilder().
|
||||
Route("GET /zones/47c0ecf6c91243308c649ad1d2d618dd/dnsrecords",
|
||||
servermock.ResponseFromFixture("records-GET.json")).
|
||||
Build(t)
|
||||
|
||||
records, err := client.GetRecords(t.Context(), "47c0ecf6c91243308c649ad1d2d618dd", &GetRecordsParameters{DNSType: "TXT", Content: `"test"'`})
|
||||
require.NoError(t, err)
|
||||
|
|
@ -134,20 +91,21 @@ func TestGetRecords(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestGetRecords_error(t *testing.T) {
|
||||
client, mux := setupTest(t)
|
||||
|
||||
mux.HandleFunc("/zones/47c0ecf6c91243308c649ad1d2d618dd/dnsrecords",
|
||||
testHandler(http.MethodGet, http.StatusUnauthorized, "error.json"))
|
||||
client := mockBuilder().
|
||||
Route("GET /zones/47c0ecf6c91243308c649ad1d2d618dd/dnsrecords",
|
||||
servermock.ResponseFromFixture("error.json").
|
||||
WithStatusCode(http.StatusUnauthorized)).
|
||||
Build(t)
|
||||
|
||||
_, err := client.GetRecords(t.Context(), "47c0ecf6c91243308c649ad1d2d618dd", &GetRecordsParameters{DNSType: "TXT", Content: `"test"'`})
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestGetRecord(t *testing.T) {
|
||||
client, mux := setupTest(t)
|
||||
|
||||
mux.HandleFunc("/zones/47c0ecf6c91243308c649ad1d2d618dd/dnsrecords/812bee17a0b440b0bd5ee099a78b839c",
|
||||
testHandler(http.MethodGet, http.StatusOK, "record-GET.json"))
|
||||
client := mockBuilder().
|
||||
Route("GET /zones/47c0ecf6c91243308c649ad1d2d618dd/dnsrecords/812bee17a0b440b0bd5ee099a78b839c",
|
||||
servermock.ResponseFromFixture("record-GET.json")).
|
||||
Build(t)
|
||||
|
||||
record, err := client.GetRecord(t.Context(), "47c0ecf6c91243308c649ad1d2d618dd", "812bee17a0b440b0bd5ee099a78b839c")
|
||||
require.NoError(t, err)
|
||||
|
|
@ -163,20 +121,22 @@ func TestGetRecord(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestGetRecord_error(t *testing.T) {
|
||||
client, mux := setupTest(t)
|
||||
|
||||
mux.HandleFunc("/zones/47c0ecf6c91243308c649ad1d2d618dd/dnsrecords/812bee17a0b440b0bd5ee099a78b839c",
|
||||
testHandler(http.MethodGet, http.StatusUnauthorized, "error.json"))
|
||||
client := mockBuilder().
|
||||
Route("GET /zones/47c0ecf6c91243308c649ad1d2d618dd/dnsrecords",
|
||||
servermock.ResponseFromFixture("error.json").
|
||||
WithStatusCode(http.StatusUnauthorized)).
|
||||
Build(t)
|
||||
|
||||
_, err := client.GetRecord(t.Context(), "47c0ecf6c91243308c649ad1d2d618dd", "812bee17a0b440b0bd5ee099a78b839c")
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestCreateRecord(t *testing.T) {
|
||||
client, mux := setupTest(t)
|
||||
|
||||
mux.HandleFunc("/zones/47c0ecf6c91243308c649ad1d2d618dd/dnsrecords",
|
||||
testHandler(http.MethodPut, http.StatusCreated, "record-PUT.json"))
|
||||
client := mockBuilder().
|
||||
Route("PUT /zones/47c0ecf6c91243308c649ad1d2d618dd/dnsrecords",
|
||||
servermock.ResponseFromFixture("record-PUT.json").
|
||||
WithStatusCode(http.StatusCreated)).
|
||||
Build(t)
|
||||
|
||||
r := Record{
|
||||
Type: "TXT",
|
||||
|
|
@ -199,10 +159,11 @@ func TestCreateRecord(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestCreateRecord_error(t *testing.T) {
|
||||
client, mux := setupTest(t)
|
||||
|
||||
mux.HandleFunc("/zones/47c0ecf6c91243308c649ad1d2d618dd/dnsrecords",
|
||||
testHandler(http.MethodPut, http.StatusUnauthorized, "error.json"))
|
||||
client := mockBuilder().
|
||||
Route("PUT /zones/47c0ecf6c91243308c649ad1d2d618dd/dnsrecords",
|
||||
servermock.ResponseFromFixture("error.json").
|
||||
WithStatusCode(http.StatusUnauthorized)).
|
||||
Build(t)
|
||||
|
||||
r := Record{
|
||||
Type: "TXT",
|
||||
|
|
@ -216,10 +177,10 @@ func TestCreateRecord_error(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestEditRecord(t *testing.T) {
|
||||
client, mux := setupTest(t)
|
||||
|
||||
mux.HandleFunc("/zones/47c0ecf6c91243308c649ad1d2d618dd/dnsrecords/eebc813de2f94d67b09d91e10e2d65c2",
|
||||
testHandler(http.MethodPatch, http.StatusOK, "record-PATCH.json"))
|
||||
client := mockBuilder().
|
||||
Route("PATCH /zones/47c0ecf6c91243308c649ad1d2d618dd/dnsrecords/eebc813de2f94d67b09d91e10e2d65c2",
|
||||
servermock.ResponseFromFixture("record-PATCH.json")).
|
||||
Build(t)
|
||||
|
||||
record, err := client.EditRecord(t.Context(), "47c0ecf6c91243308c649ad1d2d618dd", "eebc813de2f94d67b09d91e10e2d65c2", Record{
|
||||
Content: "foo",
|
||||
|
|
@ -237,10 +198,11 @@ func TestEditRecord(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestEditRecord_error(t *testing.T) {
|
||||
client, mux := setupTest(t)
|
||||
|
||||
mux.HandleFunc("/zones/47c0ecf6c91243308c649ad1d2d618dd/dnsrecords/eebc813de2f94d67b09d91e10e2d65c2",
|
||||
testHandler(http.MethodPatch, http.StatusUnauthorized, "error.json"))
|
||||
client := mockBuilder().
|
||||
Route("PATCH /zones/47c0ecf6c91243308c649ad1d2d618dd/dnsrecords/eebc813de2f94d67b09d91e10e2d65c2",
|
||||
servermock.ResponseFromFixture("error.json").
|
||||
WithStatusCode(http.StatusUnauthorized)).
|
||||
Build(t)
|
||||
|
||||
_, err := client.EditRecord(t.Context(), "47c0ecf6c91243308c649ad1d2d618dd", "eebc813de2f94d67b09d91e10e2d65c2", Record{
|
||||
Content: "foo",
|
||||
|
|
@ -249,29 +211,33 @@ func TestEditRecord_error(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestDeleteRecord(t *testing.T) {
|
||||
client, mux := setupTest(t)
|
||||
|
||||
mux.HandleFunc("/zones/47c0ecf6c91243308c649ad1d2d618dd/dnsrecords/653464211b7447a1bee6b8fcb9fb86df",
|
||||
testHandler(http.MethodDelete, http.StatusOK, "record-DELETE.json"))
|
||||
client := mockBuilder().
|
||||
Route("DELETE /zones/47c0ecf6c91243308c649ad1d2d618dd/dnsrecords/653464211b7447a1bee6b8fcb9fb86df",
|
||||
servermock.ResponseFromFixture("record-DELETE.json")).
|
||||
Build(t)
|
||||
|
||||
err := client.DeleteRecord(t.Context(), "47c0ecf6c91243308c649ad1d2d618dd", "653464211b7447a1bee6b8fcb9fb86df")
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestDeleteRecord_error(t *testing.T) {
|
||||
client, mux := setupTest(t)
|
||||
|
||||
mux.HandleFunc("/zones/47c0ecf6c91243308c649ad1d2d618dd/dnsrecords/653464211b7447a1bee6b8fcb9fb86df",
|
||||
testHandler(http.MethodDelete, http.StatusUnauthorized, "error.json"))
|
||||
client := mockBuilder().
|
||||
Route("DELETE /zones/47c0ecf6c91243308c649ad1d2d618dd/dnsrecords/653464211b7447a1bee6b8fcb9fb86df",
|
||||
servermock.ResponseFromFixture("error.json").
|
||||
WithStatusCode(http.StatusUnauthorized)).
|
||||
Build(t)
|
||||
|
||||
err := client.DeleteRecord(t.Context(), "47c0ecf6c91243308c649ad1d2d618dd", "653464211b7447a1bee6b8fcb9fb86df")
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestGetZones(t *testing.T) {
|
||||
client, mux := setupTest(t)
|
||||
|
||||
mux.HandleFunc("/", testHandler(http.MethodGet, http.StatusOK, "service-cdn-zones.json"))
|
||||
client := servermock.NewBuilder[*Client](setupClient,
|
||||
servermock.CheckHeader().
|
||||
WithBasicAuth("api", "secret"),
|
||||
).
|
||||
Route("GET /", servermock.ResponseFromFixture("service-cdn-zones.json")).
|
||||
Build(t)
|
||||
|
||||
zones, err := client.GetZones(t.Context())
|
||||
require.NoError(t, err)
|
||||
|
|
@ -302,9 +268,10 @@ func TestGetZones(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestGetZones_error(t *testing.T) {
|
||||
client, mux := setupTest(t)
|
||||
|
||||
mux.HandleFunc("/", testHandler(http.MethodGet, http.StatusUnauthorized, "error.json"))
|
||||
client := mockBuilder().
|
||||
Route("GET /", servermock.ResponseFromFixture("error.json").
|
||||
WithStatusCode(http.StatusUnauthorized)).
|
||||
Build(t)
|
||||
|
||||
_, err := client.GetZones(t.Context())
|
||||
require.Error(t, err)
|
||||
|
|
|
|||
|
|
@ -1,36 +1,30 @@
|
|||
package digitalocean
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/go-acme/lego/v4/platform/tester"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/go-acme/lego/v4/platform/tester/servermock"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var envTest = tester.NewEnvTest(EnvAuthToken)
|
||||
|
||||
func setupTest(t *testing.T) (*DNSProvider, *http.ServeMux) {
|
||||
t.Helper()
|
||||
func mockProvider() *servermock.Builder[*DNSProvider] {
|
||||
return servermock.NewBuilder(
|
||||
func(server *httptest.Server) (*DNSProvider, error) {
|
||||
config := NewDefaultConfig()
|
||||
config.AuthToken = "asdf1234"
|
||||
config.BaseURL = server.URL
|
||||
config.HTTPClient = server.Client()
|
||||
|
||||
mux := http.NewServeMux()
|
||||
server := httptest.NewServer(mux)
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
config := NewDefaultConfig()
|
||||
config.AuthToken = "asdf1234"
|
||||
config.BaseURL = server.URL
|
||||
config.HTTPClient = server.Client()
|
||||
|
||||
provider, err := NewDNSProviderConfig(config)
|
||||
require.NoError(t, err)
|
||||
|
||||
return provider, mux
|
||||
return NewDNSProviderConfig(config)
|
||||
},
|
||||
servermock.CheckHeader().
|
||||
WithJSONHeaders().
|
||||
With("Authorization", "Bearer asdf1234"))
|
||||
}
|
||||
|
||||
func TestNewDNSProvider(t *testing.T) {
|
||||
|
|
@ -111,26 +105,9 @@ func TestNewDNSProviderConfig(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestDNSProvider_Present(t *testing.T) {
|
||||
provider, mux := setupTest(t)
|
||||
|
||||
mux.HandleFunc("/v2/domains/example.com/records", func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Equal(t, http.MethodPost, r.Method, "method")
|
||||
|
||||
assert.Equal(t, "application/json", r.Header.Get("Accept"), "Accept")
|
||||
assert.Equal(t, "application/json", r.Header.Get("Content-Type"), "Content-Type")
|
||||
assert.Equal(t, "Bearer asdf1234", r.Header.Get("Authorization"), "Authorization")
|
||||
|
||||
reqBody, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
expectedReqBody := `{"type":"TXT","name":"_acme-challenge.example.com.","data":"w6uP8Tcg6K2QR905Rms8iXTlksL6OD1KOWBxTK7wxPI","ttl":30}`
|
||||
assert.Equal(t, expectedReqBody, string(bytes.TrimSpace(reqBody)))
|
||||
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
_, err = fmt.Fprintf(w, `{
|
||||
provider := mockProvider().
|
||||
Route("POST /v2/domains/example.com/records",
|
||||
servermock.RawStringResponse(`{
|
||||
"domain_record": {
|
||||
"id": 1234567,
|
||||
"type": "TXT",
|
||||
|
|
@ -140,31 +117,21 @@ func TestDNSProvider_Present(t *testing.T) {
|
|||
"port": null,
|
||||
"weight": null
|
||||
}
|
||||
}`)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
})
|
||||
}`).
|
||||
WithStatusCode(http.StatusCreated),
|
||||
servermock.CheckRequestJSONBody(`{"type":"TXT","name":"_acme-challenge.example.com.","data":"w6uP8Tcg6K2QR905Rms8iXTlksL6OD1KOWBxTK7wxPI","ttl":30}`)).
|
||||
Build(t)
|
||||
|
||||
err := provider.Present("example.com", "", "foobar")
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestDNSProvider_CleanUp(t *testing.T) {
|
||||
provider, mux := setupTest(t)
|
||||
|
||||
mux.HandleFunc("/v2/domains/example.com/records/1234567", func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Equal(t, http.MethodDelete, r.Method, "method")
|
||||
|
||||
assert.Equal(t, "/v2/domains/example.com/records/1234567", r.URL.Path, "Path")
|
||||
|
||||
assert.Equal(t, "application/json", r.Header.Get("Accept"), "Accept")
|
||||
assert.Equal(t, "application/json", r.Header.Get("Content-Type"), "Content-Type")
|
||||
assert.Equal(t, "Bearer asdf1234", r.Header.Get("Authorization"), "Authorization")
|
||||
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
})
|
||||
provider := mockProvider().
|
||||
Route("DELETE /v2/domains/example.com/records/1234567",
|
||||
servermock.Noop().
|
||||
WithStatusCode(http.StatusNoContent)).
|
||||
Build(t)
|
||||
|
||||
provider.recordIDsMu.Lock()
|
||||
provider.recordIDs["token"] = 1234567
|
||||
|
|
|
|||
|
|
@ -1,94 +1,35 @@
|
|||
package internal
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/go-acme/lego/v4/platform/tester/servermock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func setupTest(t *testing.T, pattern string, handler http.HandlerFunc) *Client {
|
||||
t.Helper()
|
||||
func mockBuilder() *servermock.Builder[*Client] {
|
||||
return servermock.NewBuilder[*Client](
|
||||
func(server *httptest.Server) (*Client, error) {
|
||||
client := NewClient(OAuthStaticAccessToken(server.Client(), "secret"))
|
||||
client.BaseURL, _ = url.Parse(server.URL)
|
||||
|
||||
mux := http.NewServeMux()
|
||||
server := httptest.NewServer(mux)
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
client := NewClient(OAuthStaticAccessToken(server.Client(), "secret"))
|
||||
client.BaseURL, _ = url.Parse(server.URL)
|
||||
|
||||
mux.HandleFunc(pattern, handler)
|
||||
|
||||
return client
|
||||
}
|
||||
|
||||
func checkHeader(req *http.Request, name, value string) error {
|
||||
val := req.Header.Get(name)
|
||||
if val != value {
|
||||
return fmt.Errorf("invalid header value, got: %s want %s", val, value)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeFixture(rw http.ResponseWriter, filename string) {
|
||||
file, err := os.Open(filepath.Join("fixtures", filename))
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer func() { _ = file.Close() }()
|
||||
|
||||
_, _ = io.Copy(rw, file)
|
||||
return client, nil
|
||||
},
|
||||
servermock.CheckHeader().WithJSONHeaders().
|
||||
WithAuthorization("Bearer secret"))
|
||||
}
|
||||
|
||||
func TestClient_AddTxtRecord(t *testing.T) {
|
||||
client := setupTest(t, "/v2/domains/example.com/records", func(rw http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != http.MethodPost {
|
||||
http.Error(rw, fmt.Sprintf("unsupported method: %s", req.Method), http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
err := checkHeader(req, "Accept", "application/json")
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
err = checkHeader(req, "Content-Type", "application/json")
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
err = checkHeader(req, "Authorization", "Bearer secret")
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
reqBody, err := io.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
expectedReqBody := `{"type":"TXT","name":"_acme-challenge.example.com.","data":"w6uP8Tcg6K2QR905Rms8iXTlksL6OD1KOWBxTK7wxPI","ttl":30}`
|
||||
if expectedReqBody != string(bytes.TrimSpace(reqBody)) {
|
||||
http.Error(rw, fmt.Sprintf("unexpected request body: %s", string(bytes.TrimSpace(reqBody))), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
rw.WriteHeader(http.StatusCreated)
|
||||
writeFixture(rw, "domains-records_POST.json")
|
||||
})
|
||||
client := mockBuilder().
|
||||
Route("POST /v2/domains/example.com/records",
|
||||
servermock.ResponseFromFixture("domains-records_POST.json").
|
||||
WithStatusCode(http.StatusCreated),
|
||||
servermock.CheckRequestJSONBody(`{"type":"TXT","name":"_acme-challenge.example.com.","data":"w6uP8Tcg6K2QR905Rms8iXTlksL6OD1KOWBxTK7wxPI","ttl":30}`)).
|
||||
Build(t)
|
||||
|
||||
record := Record{
|
||||
Type: "TXT",
|
||||
|
|
@ -112,26 +53,11 @@ func TestClient_AddTxtRecord(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_RemoveTxtRecord(t *testing.T) {
|
||||
client := setupTest(t, "/v2/domains/example.com/records/1234567", func(rw http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != http.MethodDelete {
|
||||
http.Error(rw, fmt.Sprintf("unsupported method: %s", req.Method), http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
err := checkHeader(req, "Accept", "application/json")
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
err = checkHeader(req, "Authorization", "Bearer secret")
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
rw.WriteHeader(http.StatusNoContent)
|
||||
})
|
||||
client := mockBuilder().
|
||||
Route("DELETE /v2/domains/example.com/records/1234567",
|
||||
servermock.ResponseFromFixture("domains-records_POST.json").
|
||||
WithStatusCode(http.StatusNoContent)).
|
||||
Build(t)
|
||||
|
||||
err := client.RemoveTxtRecord(t.Context(), "example.com", 1234567)
|
||||
require.NoError(t, err)
|
||||
|
|
|
|||
|
|
@ -1,88 +1,48 @@
|
|||
package internal
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/go-acme/lego/v4/platform/tester/servermock"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func setupTest(t *testing.T) (*Client, *http.ServeMux) {
|
||||
t.Helper()
|
||||
func mockBuilder() *servermock.Builder[*Client] {
|
||||
return servermock.NewBuilder[*Client](
|
||||
func(server *httptest.Server) (*Client, error) {
|
||||
client, _ := NewClient(server.URL, "user", "secret")
|
||||
client.HTTPClient = server.Client()
|
||||
|
||||
mux := http.NewServeMux()
|
||||
server := httptest.NewServer(mux)
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
client, _ := NewClient(server.URL, "user", "secret")
|
||||
client.HTTPClient = server.Client()
|
||||
|
||||
return client, mux
|
||||
return client, nil
|
||||
},
|
||||
servermock.CheckHeader().
|
||||
WithContentTypeFromURLEncoded())
|
||||
}
|
||||
|
||||
func newJSONErrorf(reason string, a ...any) string {
|
||||
err := APIError{
|
||||
func newAPIError(reason string, a ...any) APIError {
|
||||
return APIError{
|
||||
Message: "Cannot View Dns Record",
|
||||
Result: fmt.Sprintf(reason, a...),
|
||||
}
|
||||
|
||||
data, _ := json.Marshal(err)
|
||||
|
||||
return string(data)
|
||||
}
|
||||
|
||||
func testHandler(kv map[string]string) func(rw http.ResponseWriter, req *http.Request) {
|
||||
return func(rw http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != http.MethodPost {
|
||||
http.Error(rw, fmt.Sprintf("unsupported method %s", req.Method), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
domain := req.URL.Query().Get("domain")
|
||||
if domain != "example.com" {
|
||||
http.Error(rw, newJSONErrorf("invalid domain: %s", domain), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
data, err := io.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
http.Error(rw, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
values, err := url.ParseQuery(string(data))
|
||||
if err != nil {
|
||||
http.Error(rw, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
for k, v := range kv {
|
||||
actual := values.Get(k)
|
||||
if v != actual {
|
||||
http.Error(rw, newJSONErrorf("invalid %q: %s", k, actual), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_SetRecord(t *testing.T) {
|
||||
client, mux := setupTest(t)
|
||||
|
||||
kv := map[string]string{
|
||||
"action": "add",
|
||||
"name": "foo",
|
||||
"type": "TXT",
|
||||
"value": "txtTXTtxt",
|
||||
"ttl": "123",
|
||||
}
|
||||
|
||||
mux.HandleFunc("/CMD_API_DNS_CONTROL", testHandler(kv))
|
||||
client := mockBuilder().
|
||||
Route("POST /CMD_API_DNS_CONTROL", nil,
|
||||
servermock.CheckQueryParameter().Strict().
|
||||
With("domain", "example.com").
|
||||
With("json", "yes"),
|
||||
servermock.CheckForm().UsePostForm().Strict().
|
||||
With("action", "add").
|
||||
With("name", "foo").
|
||||
With("type", "TXT").
|
||||
With("value", "txtTXTtxt").
|
||||
With("ttl", "123"),
|
||||
).
|
||||
Build(t)
|
||||
|
||||
record := Record{
|
||||
Name: "foo",
|
||||
|
|
@ -96,11 +56,11 @@ func TestClient_SetRecord(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_SetRecord_error(t *testing.T) {
|
||||
client, mux := setupTest(t)
|
||||
|
||||
mux.HandleFunc("/CMD_API_DNS_CONTROL", func(rw http.ResponseWriter, req *http.Request) {
|
||||
http.Error(rw, newJSONErrorf("OOPS"), http.StatusInternalServerError)
|
||||
})
|
||||
client := mockBuilder().
|
||||
Route("POST /CMD_API_DNS_CONTROL",
|
||||
servermock.JSONEncode(newAPIError("OOPS")).
|
||||
WithStatusCode(http.StatusInternalServerError)).
|
||||
Build(t)
|
||||
|
||||
record := Record{
|
||||
Name: "foo",
|
||||
|
|
@ -114,17 +74,18 @@ func TestClient_SetRecord_error(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_DeleteRecord(t *testing.T) {
|
||||
client, mux := setupTest(t)
|
||||
|
||||
kv := map[string]string{
|
||||
"action": "delete",
|
||||
"name": "foo",
|
||||
"type": "TXT",
|
||||
"value": "txtTXTtxt",
|
||||
"ttl": "",
|
||||
}
|
||||
|
||||
mux.HandleFunc("/CMD_API_DNS_CONTROL", testHandler(kv))
|
||||
client := mockBuilder().
|
||||
Route("POST /CMD_API_DNS_CONTROL", nil,
|
||||
servermock.CheckQueryParameter().Strict().
|
||||
With("domain", "example.com").
|
||||
With("json", "yes"),
|
||||
servermock.CheckForm().UsePostForm().Strict().
|
||||
With("action", "delete").
|
||||
With("name", "foo").
|
||||
With("type", "TXT").
|
||||
With("value", "txtTXTtxt"),
|
||||
).
|
||||
Build(t)
|
||||
|
||||
record := Record{
|
||||
Name: "foo",
|
||||
|
|
@ -137,11 +98,11 @@ func TestClient_DeleteRecord(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_DeleteRecord_error(t *testing.T) {
|
||||
client, mux := setupTest(t)
|
||||
|
||||
mux.HandleFunc("/CMD_API_DNS_CONTROL", func(rw http.ResponseWriter, req *http.Request) {
|
||||
http.Error(rw, newJSONErrorf("OOPS"), http.StatusInternalServerError)
|
||||
})
|
||||
client := mockBuilder().
|
||||
Route("POST /CMD_API_DNS_CONTROL",
|
||||
servermock.JSONEncode(newAPIError("OOPS")).
|
||||
WithStatusCode(http.StatusInternalServerError)).
|
||||
Build(t)
|
||||
|
||||
record := Record{
|
||||
Name: "foo",
|
||||
|
|
|
|||
|
|
@ -2,33 +2,32 @@ package internal
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/go-acme/lego/v4/platform/tester/servermock"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func setupTest(t *testing.T, credentials map[string]string, handler http.HandlerFunc) *Client {
|
||||
t.Helper()
|
||||
func setupClient(credentials map[string]string) func(server *httptest.Server) (*Client, error) {
|
||||
return func(server *httptest.Server) (*Client, error) {
|
||||
client := NewClient(credentials)
|
||||
client.HTTPClient = server.Client()
|
||||
client.baseURL = server.URL
|
||||
|
||||
mux := http.NewServeMux()
|
||||
server := httptest.NewServer(mux)
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
mux.HandleFunc("/", handler)
|
||||
|
||||
client := NewClient(credentials)
|
||||
client.HTTPClient = server.Client()
|
||||
client.baseURL = server.URL
|
||||
|
||||
return client
|
||||
return client, nil
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_Add(t *testing.T) {
|
||||
txtValue := "123456789012"
|
||||
|
||||
client := setupTest(t, map[string]string{"example.org": "secret"}, handlerMock(addAction, txtValue))
|
||||
client := servermock.NewBuilder[*Client](setupClient(map[string]string{"example.org": "secret"})).
|
||||
Route("POST /",
|
||||
servermock.RawStringResponse(fmt.Sprintf("%s %s", successCode, txtValue)),
|
||||
servermock.CheckQueryParameter().Strict().
|
||||
With("acme", addAction).With("txt", txtValue)).
|
||||
Build(t)
|
||||
|
||||
err := client.Add(t.Context(), "example.org", txtValue)
|
||||
require.NoError(t, err)
|
||||
|
|
@ -37,16 +36,27 @@ func TestClient_Add(t *testing.T) {
|
|||
func TestClient_Add_error(t *testing.T) {
|
||||
txtValue := "123456789012"
|
||||
|
||||
client := setupTest(t, map[string]string{"example.com": "secret"}, handlerMock(addAction, txtValue))
|
||||
client := servermock.NewBuilder[*Client](setupClient(map[string]string{"example.com": "secret"})).
|
||||
Route("POST /",
|
||||
servermock.RawStringResponse(fmt.Sprintf("%s %s", successCode, txtValue)),
|
||||
servermock.CheckQueryParameter().Strict().
|
||||
With("acme", addAction).With("txt", txtValue)).
|
||||
Build(t)
|
||||
|
||||
err := client.Add(t.Context(), "example.org", txtValue)
|
||||
require.Error(t, err)
|
||||
|
||||
require.EqualError(t, err, "domain example.org not found in credentials, check your credentials map")
|
||||
}
|
||||
|
||||
func TestClient_Remove(t *testing.T) {
|
||||
txtValue := "ABCDEFGHIJKL"
|
||||
|
||||
client := setupTest(t, map[string]string{"example.org": "secret"}, handlerMock(removeAction, txtValue))
|
||||
client := servermock.NewBuilder[*Client](setupClient(map[string]string{"example.org": "secret"})).
|
||||
Route("POST /",
|
||||
servermock.RawStringResponse(fmt.Sprintf("%s %s", successCode, txtValue)),
|
||||
servermock.CheckQueryParameter().Strict().
|
||||
With("acme", removeAction).With("txt", txtValue)).
|
||||
Build(t)
|
||||
|
||||
err := client.Remove(t.Context(), "example.org", txtValue)
|
||||
require.NoError(t, err)
|
||||
|
|
@ -55,34 +65,45 @@ func TestClient_Remove(t *testing.T) {
|
|||
func TestClient_Remove_error(t *testing.T) {
|
||||
txtValue := "ABCDEFGHIJKL"
|
||||
|
||||
client := setupTest(t, map[string]string{"example.com": "secret"}, handlerMock(removeAction, txtValue))
|
||||
testCases := []struct {
|
||||
desc string
|
||||
hostname string
|
||||
response string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
desc: "response error - txt",
|
||||
hostname: "example.com",
|
||||
response: "error - no valid acme txt record",
|
||||
expected: "error - no valid acme txt record",
|
||||
},
|
||||
{
|
||||
desc: "response error - acme",
|
||||
hostname: "example.com",
|
||||
response: "nochg 1234:1234:1234:1234:1234:1234:1234:1234",
|
||||
expected: "nochg 1234:1234:1234:1234:1234:1234:1234:1234",
|
||||
},
|
||||
{
|
||||
desc: "credential error",
|
||||
hostname: "example.org",
|
||||
response: fmt.Sprintf("%s %s", successCode, txtValue),
|
||||
expected: "domain example.org not found in credentials, check your credentials map",
|
||||
},
|
||||
}
|
||||
|
||||
err := client.Remove(t.Context(), "example.org", txtValue)
|
||||
require.Error(t, err)
|
||||
}
|
||||
for _, test := range testCases {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
func handlerMock(action, value string) http.HandlerFunc {
|
||||
return func(rw http.ResponseWriter, req *http.Request) {
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
client := servermock.NewBuilder[*Client](setupClient(map[string]string{"example.com": "secret"})).
|
||||
Route("POST /",
|
||||
servermock.RawStringResponse(test.response),
|
||||
servermock.CheckQueryParameter().Strict().
|
||||
With("acme", removeAction).With("txt", txtValue)).
|
||||
Build(t)
|
||||
|
||||
query := req.URL.Query()
|
||||
|
||||
if query.Get("acme") != action {
|
||||
_, _ = rw.Write([]byte("nochg 1234:1234:1234:1234:1234:1234:1234:1234"))
|
||||
return
|
||||
}
|
||||
|
||||
txtValue := query.Get("txt")
|
||||
if len(txtValue) < 12 {
|
||||
_, _ = rw.Write([]byte("error - no valid acme txt record"))
|
||||
return
|
||||
}
|
||||
|
||||
if txtValue != value {
|
||||
http.Error(rw, fmt.Sprintf("got: %q, expected: %q", txtValue, value), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
_, _ = fmt.Fprintf(rw, "%s %s", successCode, txtValue)
|
||||
err := client.Remove(t.Context(), test.hostname, txtValue)
|
||||
require.EqualError(t, err, test.expected)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ Always returns StatusOK (200)
|
|||
|
||||
If the API call works the first word of the response body is `successfully`.
|
||||
|
||||
If an error encoured the response body is `error - <ERRMSG>`.
|
||||
If an error occurs the response body is `error - <ERRMSG>`.
|
||||
|
||||
Can be a POST or a GET.
|
||||
|
||||
|
|
@ -35,6 +35,6 @@ Always returns StatusOK (200)
|
|||
|
||||
If the API call works the first word of the response body is `successfully`.
|
||||
|
||||
If an error encoured the response body is `error - <ERRMSG>`.
|
||||
If an error occurs the response body is `error - <ERRMSG>`.
|
||||
|
||||
Can be a POST or a GET.
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import (
|
|||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/challenge/dns01"
|
||||
"github.com/go-acme/lego/v4/providers/dns/internal/errutils"
|
||||
)
|
||||
|
||||
|
|
@ -57,10 +58,8 @@ func NewClient(apiKey, apiSecret string) (*Client, error) {
|
|||
func (c *Client) GetDomain(ctx context.Context, authZone string) (*Domain, error) {
|
||||
endpoint := c.BaseURL.JoinPath("dns", "managed", "name")
|
||||
|
||||
domainName := authZone[0 : len(authZone)-1]
|
||||
|
||||
query := endpoint.Query()
|
||||
query.Set("domainname", domainName)
|
||||
query.Set("domainname", dns01.UnFqdn(authZone))
|
||||
endpoint.RawQuery = query.Encode()
|
||||
|
||||
req, err := newJSONRequest(ctx, http.MethodGet, endpoint, nil)
|
||||
|
|
|
|||
|
|
@ -2,14 +2,132 @@ package internal
|
|||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/platform/tester/servermock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_sign(t *testing.T) {
|
||||
func mockBuilder() *servermock.Builder[*Client] {
|
||||
return servermock.NewBuilder[*Client](
|
||||
func(server *httptest.Server) (*Client, error) {
|
||||
client, err := NewClient("key", "secret")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client.BaseURL, _ = url.Parse(server.URL)
|
||||
client.HTTPClient = server.Client()
|
||||
|
||||
return client, nil
|
||||
},
|
||||
servermock.CheckHeader().WithJSONHeaders().
|
||||
With("x-dnsme-apiKey", "key").
|
||||
WithRegexp("x-dnsme-requestDate", `\w+, \d+ \w+ \d+ \d+:\d+:\d+ UTC`).
|
||||
WithRegexp("x-dnsme-hmac", `[a-z0-9]+`),
|
||||
)
|
||||
}
|
||||
|
||||
func TestClient_GetDomain(t *testing.T) {
|
||||
client := mockBuilder().
|
||||
Route("GET /dns/managed/name",
|
||||
servermock.RawStringResponse(`{"id": 1, "name": "foo"}`),
|
||||
servermock.CheckQueryParameter().Strict().
|
||||
With("domainname", "example.com")).
|
||||
Build(t)
|
||||
|
||||
domain, err := client.GetDomain(t.Context(), "example.com.")
|
||||
require.NoError(t, err)
|
||||
|
||||
expected := &Domain{
|
||||
ID: 1,
|
||||
Name: "foo",
|
||||
}
|
||||
|
||||
assert.Equal(t, expected, domain)
|
||||
}
|
||||
|
||||
func TestClient_GetRecords(t *testing.T) {
|
||||
client := mockBuilder().
|
||||
Route("GET /dns/managed/1/records",
|
||||
servermock.ResponseFromFixture("get_records.json"),
|
||||
servermock.CheckQueryParameter().Strict().
|
||||
With("recordName", "foo").
|
||||
With("type", "TXT"),
|
||||
).
|
||||
Build(t)
|
||||
|
||||
domain := &Domain{ID: 1, Name: "foo"}
|
||||
|
||||
records, err := client.GetRecords(t.Context(), domain, "foo", "TXT")
|
||||
require.NoError(t, err)
|
||||
|
||||
expected := []Record{
|
||||
{
|
||||
ID: 1,
|
||||
Type: "TXT",
|
||||
Name: "foo",
|
||||
Value: "aaa",
|
||||
TTL: 60,
|
||||
SourceID: 123,
|
||||
},
|
||||
{
|
||||
ID: 2,
|
||||
Type: "TXT",
|
||||
Name: "bar",
|
||||
Value: "bbb",
|
||||
TTL: 120,
|
||||
SourceID: 456,
|
||||
},
|
||||
}
|
||||
|
||||
assert.Equal(t, &expected, records)
|
||||
}
|
||||
|
||||
func TestClient_CreateRecord(t *testing.T) {
|
||||
client := mockBuilder().
|
||||
Route("POST /dns/managed/1/records", nil,
|
||||
servermock.CheckRequestJSONBodyFromFile("create_record-request.json")).
|
||||
Build(t)
|
||||
|
||||
domain := &Domain{ID: 1, Name: "foo"}
|
||||
|
||||
record := &Record{
|
||||
ID: 1,
|
||||
Type: "TXT",
|
||||
Name: "foo",
|
||||
Value: "aaa",
|
||||
TTL: 60,
|
||||
SourceID: 123,
|
||||
}
|
||||
|
||||
err := client.CreateRecord(t.Context(), domain, record)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestClient_DeleteRecord(t *testing.T) {
|
||||
client := mockBuilder().
|
||||
Route("DELETE /dns/managed/123/records/1", nil).
|
||||
Build(t)
|
||||
|
||||
record := Record{
|
||||
ID: 1,
|
||||
Type: "TXT",
|
||||
Name: "foo",
|
||||
Value: "aaa",
|
||||
TTL: 60,
|
||||
SourceID: 123,
|
||||
}
|
||||
|
||||
err := client.DeleteRecord(t.Context(), record)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestClient_sign(t *testing.T) {
|
||||
apiKey := "key"
|
||||
|
||||
client := Client{apiKey: apiKey, apiSecret: "secret"}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"id": 1,
|
||||
"type": "TXT",
|
||||
"name": "foo",
|
||||
"value": "aaa",
|
||||
"ttl": 60,
|
||||
"sourceId": 123
|
||||
}
|
||||
20
providers/dns/dnsmadeeasy/internal/fixtures/get_records.json
Normal file
20
providers/dns/dnsmadeeasy/internal/fixtures/get_records.json
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"data": [
|
||||
{
|
||||
"id": 1,
|
||||
"type": "TXT",
|
||||
"name": "foo",
|
||||
"value": "aaa",
|
||||
"ttl": 60,
|
||||
"sourceId": 123
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"type": "TXT",
|
||||
"name": "bar",
|
||||
"value": "bbb",
|
||||
"ttl": 120,
|
||||
"sourceId": 456
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,91 +1,43 @@
|
|||
package internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/go-acme/lego/v4/platform/tester/servermock"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func setupTest(t *testing.T, method, pattern string, status int, file string) *Client {
|
||||
t.Helper()
|
||||
|
||||
mux := http.NewServeMux()
|
||||
server := httptest.NewServer(mux)
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
mux.HandleFunc(pattern, func(rw http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != method {
|
||||
http.Error(rw, fmt.Sprintf("unsupported method: %s", req.Method), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
query := req.URL.Query()
|
||||
if query.Get("token") != "secret" {
|
||||
http.Error(rw, fmt.Sprintf("invalid credentials: %q", query.Get("token")), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
if query.Get("domain") != "example.com" {
|
||||
http.Error(rw, fmt.Sprintf("invalid domain: %q", query.Get("domain")), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if query.Has("action") {
|
||||
if query.Get("action") != "delete" {
|
||||
http.Error(rw, fmt.Sprintf("invalid action: %q", query.Get("action")), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if query.Get("value") != "value" {
|
||||
http.Error(rw, fmt.Sprintf("invalid value: %q", query.Get("value")), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if file == "" {
|
||||
rw.WriteHeader(status)
|
||||
return
|
||||
}
|
||||
|
||||
open, err := os.Open(filepath.Join("fixtures", file))
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
defer func() { _ = open.Close() }()
|
||||
|
||||
rw.WriteHeader(status)
|
||||
_, err = io.Copy(rw, open)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
func setupClient(server *httptest.Server) (*Client, error) {
|
||||
client := NewClient("secret")
|
||||
client.HTTPClient = server.Client()
|
||||
client.baseURL, _ = url.Parse(server.URL)
|
||||
|
||||
return client
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func TestClient_UpdateTxtRecord(t *testing.T) {
|
||||
client := setupTest(t, http.MethodGet, "/letsencrypt", http.StatusOK, "success.json")
|
||||
client := servermock.NewBuilder[*Client](setupClient).
|
||||
Route("GET /letsencrypt", servermock.ResponseFromFixture("success.json"),
|
||||
servermock.CheckQueryParameter().Strict().
|
||||
With("domain", "example.com").
|
||||
With("token", "secret").
|
||||
With("value", "value")).
|
||||
Build(t)
|
||||
|
||||
err := client.UpdateTxtRecord(t.Context(), "example.com.", "value", false)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestClient_UpdateTxtRecord_clear(t *testing.T) {
|
||||
client := setupTest(t, http.MethodGet, "/letsencrypt", http.StatusOK, "success.json")
|
||||
client := servermock.NewBuilder[*Client](setupClient).
|
||||
Route("GET /letsencrypt", servermock.ResponseFromFixture("success.json"),
|
||||
servermock.CheckQueryParameter().Strict().
|
||||
With("action", "delete").
|
||||
With("domain", "example.com").
|
||||
With("token", "secret")).
|
||||
Build(t)
|
||||
|
||||
err := client.UpdateTxtRecord(t.Context(), "example.com.", "value", true)
|
||||
require.NoError(t, err)
|
||||
|
|
|
|||
|
|
@ -1,121 +1,56 @@
|
|||
package internal
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/go-acme/lego/v4/platform/tester/servermock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
const authorizationHeader = "Authorization"
|
||||
func mockBuilder() *servermock.Builder[*Client] {
|
||||
return servermock.NewBuilder[*Client](
|
||||
func(server *httptest.Server) (*Client, error) {
|
||||
client := NewClient("token", "secret")
|
||||
client.HTTPClient = server.Client()
|
||||
client.baseURL, _ = url.Parse(server.URL)
|
||||
|
||||
func setupTest(t *testing.T) (*Client, *http.ServeMux) {
|
||||
t.Helper()
|
||||
|
||||
mux := http.NewServeMux()
|
||||
server := httptest.NewServer(mux)
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
client := NewClient("token", "secret")
|
||||
client.HTTPClient = server.Client()
|
||||
client.baseURL, _ = url.Parse(server.URL)
|
||||
|
||||
return client, mux
|
||||
return client, nil
|
||||
},
|
||||
servermock.CheckHeader().WithJSONHeaders().
|
||||
WithBasicAuth("token", "secret"),
|
||||
)
|
||||
}
|
||||
|
||||
func TestClient_CreateTXTRecord(t *testing.T) {
|
||||
client, mux := setupTest(t)
|
||||
client := mockBuilder().
|
||||
Route("POST /domains/1/dns",
|
||||
servermock.ResponseFromFixture("create_record.json"),
|
||||
servermock.CheckRequestJSONBodyFromFile("create_record-request.json")).
|
||||
Build(t)
|
||||
|
||||
mux.HandleFunc("/domains/1/dns", func(rw http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != http.MethodPost {
|
||||
http.Error(rw, "invalid method: "+req.Method, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
auth := req.Header.Get(authorizationHeader)
|
||||
if auth != "Basic dG9rZW46c2VjcmV0" {
|
||||
http.Error(rw, "invalid credentials: "+auth, http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
_, _ = rw.Write([]byte(`{"id": 1}`))
|
||||
})
|
||||
|
||||
err := client.CreateTXTRecord(t.Context(), &Domain{ID: 1}, "example", "txtTXTtxt")
|
||||
err := client.CreateTXTRecord(t.Context(), &Domain{ID: 1}, "example.com", "txtTXTtxt")
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestClient_DeleteTXTRecord(t *testing.T) {
|
||||
client, mux := setupTest(t)
|
||||
|
||||
mux.HandleFunc("/domains/1/dns", func(rw http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != http.MethodGet {
|
||||
http.Error(rw, "invalid method: "+req.Method, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
auth := req.Header.Get(authorizationHeader)
|
||||
if auth != "Basic dG9rZW46c2VjcmV0" {
|
||||
http.Error(rw, "invalid credentials: "+auth, http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
_, _ = rw.Write([]byte(`[
|
||||
{
|
||||
"id": 1,
|
||||
"host": "example.com",
|
||||
"ttl": 3600,
|
||||
"type": "TXT",
|
||||
"data": "txtTXTtxt"
|
||||
}
|
||||
]`))
|
||||
})
|
||||
|
||||
mux.HandleFunc("/domains/1/dns/1", func(rw http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != http.MethodDelete {
|
||||
http.Error(rw, "invalid method: "+req.Method, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
auth := req.Header.Get(authorizationHeader)
|
||||
if auth != "Basic dG9rZW46c2VjcmV0" {
|
||||
http.Error(rw, "invalid credentials: "+auth, http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
})
|
||||
client := mockBuilder().
|
||||
Route("GET /domains/1/dns",
|
||||
servermock.ResponseFromFixture("delete_record.json")).
|
||||
Route("DELETE /domains/1/dns/1", nil).
|
||||
Build(t)
|
||||
|
||||
err := client.DeleteTXTRecord(t.Context(), &Domain{ID: 1}, "example.com", "txtTXTtxt")
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestClient_getDNSRecordByHostData(t *testing.T) {
|
||||
client, mux := setupTest(t)
|
||||
|
||||
mux.HandleFunc("/domains/1/dns", func(rw http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != http.MethodGet {
|
||||
http.Error(rw, "invalid method: "+req.Method, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
auth := req.Header.Get(authorizationHeader)
|
||||
if auth != "Basic dG9rZW46c2VjcmV0" {
|
||||
http.Error(rw, "invalid credentials: "+auth, http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
_, _ = rw.Write([]byte(`[
|
||||
{
|
||||
"id": 1,
|
||||
"host": "example.com",
|
||||
"ttl": 3600,
|
||||
"type": "TXT",
|
||||
"data": "txtTXTtxt"
|
||||
}
|
||||
]`))
|
||||
})
|
||||
client := mockBuilder().
|
||||
Route("GET /domains/1/dns",
|
||||
servermock.ResponseFromFixture("getDnsRecords.json")).
|
||||
Build(t)
|
||||
|
||||
record, err := client.getDNSRecordByHostData(t.Context(), Domain{ID: 1}, "example.com", "txtTXTtxt")
|
||||
require.NoError(t, err)
|
||||
|
|
@ -132,43 +67,10 @@ func TestClient_getDNSRecordByHostData(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_GetDomainByName(t *testing.T) {
|
||||
client, mux := setupTest(t)
|
||||
|
||||
mux.HandleFunc("/domains", func(rw http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != http.MethodGet {
|
||||
http.Error(rw, "invalid method: "+req.Method, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
auth := req.Header.Get(authorizationHeader)
|
||||
if auth != "Basic dG9rZW46c2VjcmV0" {
|
||||
http.Error(rw, "invalid credentials: "+auth, http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
_, _ = rw.Write([]byte(`[
|
||||
{
|
||||
"id": 1,
|
||||
"domain": "example.com",
|
||||
"expiry_date": "2019-08-24",
|
||||
"registered_date": "2019-08-24",
|
||||
"renew": true,
|
||||
"registrant": "Ola Nordmann",
|
||||
"status": "active",
|
||||
"nameservers": [
|
||||
"ns1.hyp.net",
|
||||
"ns2.hyp.net",
|
||||
"ns3.hyp.net"
|
||||
],
|
||||
"services": {
|
||||
"registrar": true,
|
||||
"dns": true,
|
||||
"email": true,
|
||||
"webhotel": "none"
|
||||
}
|
||||
}
|
||||
]`))
|
||||
})
|
||||
client := mockBuilder().
|
||||
Route("GET /domains/",
|
||||
servermock.ResponseFromFixture("getDomains.json")).
|
||||
Build(t)
|
||||
|
||||
domain, err := client.GetDomainByName(t.Context(), "example.com")
|
||||
require.NoError(t, err)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"data": "txtTXTtxt",
|
||||
"host": "example.com",
|
||||
"id": 0,
|
||||
"ttl": 300,
|
||||
"type": "TXT"
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"id": 1
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
[
|
||||
{
|
||||
"id": 1,
|
||||
"host": "example.com",
|
||||
"ttl": 3600,
|
||||
"type": "TXT",
|
||||
"data": "txtTXTtxt"
|
||||
}
|
||||
]
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
[
|
||||
{
|
||||
"id": 1,
|
||||
"host": "example.com",
|
||||
"ttl": 3600,
|
||||
"type": "TXT",
|
||||
"data": "txtTXTtxt"
|
||||
}
|
||||
]
|
||||
22
providers/dns/domeneshop/internal/fixtures/getDomains.json
Normal file
22
providers/dns/domeneshop/internal/fixtures/getDomains.json
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
[
|
||||
{
|
||||
"id": 1,
|
||||
"domain": "example.com",
|
||||
"expiry_date": "2019-08-24",
|
||||
"registered_date": "2019-08-24",
|
||||
"renew": true,
|
||||
"registrant": "Ola Nordmann",
|
||||
"status": "active",
|
||||
"nameservers": [
|
||||
"ns1.hyp.net",
|
||||
"ns2.hyp.net",
|
||||
"ns3.hyp.net"
|
||||
],
|
||||
"services": {
|
||||
"registrar": true,
|
||||
"dns": true,
|
||||
"email": true,
|
||||
"webhotel": "none"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
@ -1,13 +1,12 @@
|
|||
package dreamhost
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/platform/tester"
|
||||
"github.com/go-acme/lego/v4/platform/tester/servermock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
|
@ -23,22 +22,15 @@ const (
|
|||
fakeKeyAuth = "w6uP8Tcg6K2QR905Rms8iXTlksL6OD1KOWBxTK7wxPI"
|
||||
)
|
||||
|
||||
func setupTest(t *testing.T) (*DNSProvider, *http.ServeMux) {
|
||||
t.Helper()
|
||||
func mockBuilder() *servermock.Builder[*DNSProvider] {
|
||||
return servermock.NewBuilder(func(server *httptest.Server) (*DNSProvider, error) {
|
||||
config := NewDefaultConfig()
|
||||
config.APIKey = fakeAPIKey
|
||||
config.BaseURL = server.URL
|
||||
config.HTTPClient = server.Client()
|
||||
|
||||
mux := http.NewServeMux()
|
||||
server := httptest.NewServer(mux)
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
config := NewDefaultConfig()
|
||||
config.APIKey = fakeAPIKey
|
||||
config.BaseURL = server.URL
|
||||
config.HTTPClient = server.Client()
|
||||
|
||||
provider, err := NewDNSProviderConfig(config)
|
||||
require.NoError(t, err)
|
||||
|
||||
return provider, mux
|
||||
return NewDNSProviderConfig(config)
|
||||
})
|
||||
}
|
||||
|
||||
func TestNewDNSProvider(t *testing.T) {
|
||||
|
|
@ -115,67 +107,48 @@ func TestNewDNSProviderConfig(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestDNSProvider_Present(t *testing.T) {
|
||||
provider, mux := setupTest(t)
|
||||
|
||||
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Equal(t, http.MethodGet, r.Method, "method")
|
||||
|
||||
q := r.URL.Query()
|
||||
assert.Equal(t, fakeAPIKey, q.Get("key"))
|
||||
assert.Equal(t, "dns-add_record", q.Get("cmd"))
|
||||
assert.Equal(t, "json", q.Get("format"))
|
||||
assert.Equal(t, "_acme-challenge.example.com", q.Get("record"))
|
||||
assert.Equal(t, fakeKeyAuth, q.Get("value"))
|
||||
assert.Equal(t, "Managed+By+lego", q.Get("comment"))
|
||||
|
||||
_, err := fmt.Fprintf(w, `{"data":"record_added","result":"success"}`)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
})
|
||||
provider := mockBuilder().
|
||||
Route("GET /",
|
||||
servermock.RawStringResponse(`{"data":"record_added","result":"success"}`),
|
||||
servermock.CheckQueryParameter().Strict().
|
||||
With("cmd", "dns-add_record").
|
||||
With("comment", "Managed+By+lego").
|
||||
With("format", "json").
|
||||
With("record", "_acme-challenge.example.com").
|
||||
With("type", "TXT").
|
||||
With("key", fakeAPIKey).
|
||||
With("value", fakeKeyAuth),
|
||||
).
|
||||
Build(t)
|
||||
|
||||
err := provider.Present("example.com", "", fakeChallengeToken)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestDNSProvider_PresentFailed(t *testing.T) {
|
||||
provider, mux := setupTest(t)
|
||||
|
||||
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Equal(t, http.MethodGet, r.Method, "method")
|
||||
|
||||
_, err := fmt.Fprintf(w, `{"data":"record_already_exists_remove_first","result":"error"}`)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
})
|
||||
provider := mockBuilder().
|
||||
Route("GET /",
|
||||
servermock.RawStringResponse(`{"data":"record_already_exists_remove_first","result":"error"}`)).
|
||||
Build(t)
|
||||
|
||||
err := provider.Present("example.com", "", fakeChallengeToken)
|
||||
require.EqualError(t, err, "dreamhost: add TXT record failed: record_already_exists_remove_first")
|
||||
}
|
||||
|
||||
func TestDNSProvider_Cleanup(t *testing.T) {
|
||||
provider, mux := setupTest(t)
|
||||
|
||||
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Equal(t, http.MethodGet, r.Method, "method")
|
||||
|
||||
q := r.URL.Query()
|
||||
assert.Equal(t, fakeAPIKey, q.Get("key"), "key mismatch")
|
||||
assert.Equal(t, "dns-remove_record", q.Get("cmd"), "cmd mismatch")
|
||||
assert.Equal(t, "json", q.Get("format"))
|
||||
assert.Equal(t, "_acme-challenge.example.com", q.Get("record"))
|
||||
assert.Equal(t, fakeKeyAuth, q.Get("value"), "value mismatch")
|
||||
assert.Equal(t, "Managed+By+lego", q.Get("comment"))
|
||||
|
||||
_, err := fmt.Fprintf(w, `{"data":"record_removed","result":"success"}`)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
})
|
||||
provider := mockBuilder().
|
||||
Route("GET /",
|
||||
servermock.RawStringResponse(`{"data":"record_removed","result":"success"}`),
|
||||
servermock.CheckQueryParameter().Strict().
|
||||
With("cmd", "dns-remove_record").
|
||||
With("comment", "Managed+By+lego").
|
||||
With("format", "json").
|
||||
With("record", "_acme-challenge.example.com").
|
||||
With("type", "TXT").
|
||||
With("key", fakeAPIKey).
|
||||
With("value", fakeKeyAuth),
|
||||
).
|
||||
Build(t)
|
||||
|
||||
err := provider.CleanUp("example.com", "", fakeChallengeToken)
|
||||
require.NoError(t, err, "failed to remove TXT record")
|
||||
|
|
|
|||
|
|
@ -1,15 +1,59 @@
|
|||
package internal
|
||||
|
||||
import (
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/go-acme/lego/v4/platform/tester/servermock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
const fakeAPIKey = "asdf1234"
|
||||
func setupClient(server *httptest.Server) (*Client, error) {
|
||||
client := NewClient("secret")
|
||||
client.BaseURL = server.URL
|
||||
client.HTTPClient = server.Client()
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func TestClient_AddRecord(t *testing.T) {
|
||||
client := servermock.NewBuilder[*Client](setupClient).
|
||||
Route("GET /", servermock.RawStringResponse(`{}`),
|
||||
servermock.CheckQueryParameter().Strict().
|
||||
With("cmd", "dns-add_record").
|
||||
With("comment", "Managed+By+lego").
|
||||
With("format", "json").
|
||||
With("key", "secret").
|
||||
With("record", "example.com").
|
||||
With("type", "TXT").
|
||||
With("value", "aaa")).
|
||||
Build(t)
|
||||
|
||||
err := client.AddRecord(t.Context(), "example.com", "aaa")
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestClient_RemoveRecord(t *testing.T) {
|
||||
client := servermock.NewBuilder[*Client](setupClient).
|
||||
Route("GET /", servermock.RawStringResponse(`{}`),
|
||||
servermock.CheckQueryParameter().Strict().
|
||||
With("cmd", "dns-remove_record").
|
||||
With("comment", "Managed+By+lego").
|
||||
With("format", "json").
|
||||
With("key", "secret").
|
||||
With("record", "example.com").
|
||||
With("type", "TXT").
|
||||
With("value", "aaa")).
|
||||
Build(t)
|
||||
|
||||
err := client.RemoveRecord(t.Context(), "example.com", "aaa")
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestClient_buildQuery(t *testing.T) {
|
||||
const fakeAPIKey = "asdf1234"
|
||||
|
||||
testCases := []struct {
|
||||
desc string
|
||||
apiKey string
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ const defaultBaseURL = "https://www.duckdns.org/update"
|
|||
type Client struct {
|
||||
token string
|
||||
|
||||
baseURL string
|
||||
HTTPClient *http.Client
|
||||
}
|
||||
|
||||
|
|
@ -28,6 +29,7 @@ type Client struct {
|
|||
func NewClient(token string) *Client {
|
||||
return &Client{
|
||||
token: token,
|
||||
baseURL: defaultBaseURL,
|
||||
HTTPClient: &http.Client{Timeout: 5 * time.Second},
|
||||
}
|
||||
}
|
||||
|
|
@ -44,7 +46,7 @@ func (c Client) RemoveTXTRecord(ctx context.Context, domain string) error {
|
|||
// To update the TXT record we just need to make one simple get request.
|
||||
// In DuckDNS you only have one TXT record shared with the domain and all subdomains.
|
||||
func (c Client) UpdateTxtRecord(ctx context.Context, domain, txt string, clearRecord bool) error {
|
||||
endpoint, _ := url.Parse(defaultBaseURL)
|
||||
endpoint, _ := url.Parse(c.baseURL)
|
||||
|
||||
mainDomain := getMainDomain(domain)
|
||||
if mainDomain == "" {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,50 @@
|
|||
package internal
|
||||
|
||||
import (
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/go-acme/lego/v4/platform/tester/servermock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func setupClient(server *httptest.Server) (*Client, error) {
|
||||
client := NewClient("secret")
|
||||
client.baseURL = server.URL
|
||||
client.HTTPClient = server.Client()
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func TestClient_AddTXTRecord(t *testing.T) {
|
||||
client := servermock.NewBuilder[*Client](setupClient).
|
||||
Route("GET /", servermock.RawStringResponse("OK"),
|
||||
servermock.CheckQueryParameter().Strict().
|
||||
With("clear", "false").
|
||||
With("domains", "com").
|
||||
With("token", "secret").
|
||||
With("txt", "value")).
|
||||
Build(t)
|
||||
|
||||
err := client.AddTXTRecord(t.Context(), "example.com", "value")
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestClient_RemoveTXTRecord(t *testing.T) {
|
||||
client := servermock.NewBuilder[*Client](setupClient).
|
||||
Route("GET /", servermock.RawStringResponse("OK"),
|
||||
servermock.CheckQueryParameter().Strict().
|
||||
With("clear", "true").
|
||||
With("domains", "com").
|
||||
With("token", "secret").
|
||||
With("txt", "")).
|
||||
Build(t)
|
||||
|
||||
err := client.RemoveTXTRecord(t.Context(), "example.com")
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func Test_getMainDomain(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
|
|
|
|||
|
|
@ -1,120 +1,58 @@
|
|||
package internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/go-acme/lego/v4/platform/tester/servermock"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func setupTest(t *testing.T, pattern string, handlerFunc http.HandlerFunc) *Client {
|
||||
t.Helper()
|
||||
|
||||
mux := http.NewServeMux()
|
||||
server := httptest.NewServer(mux)
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
mux.HandleFunc(pattern, handlerFunc)
|
||||
|
||||
func setupClient(server *httptest.Server) (*Client, error) {
|
||||
client := NewClient("bob", "user", "secret")
|
||||
client.HTTPClient = server.Client()
|
||||
client.baseURL, _ = url.Parse(server.URL)
|
||||
|
||||
return client
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func authenticatedHandler(method string, status int, file string) http.HandlerFunc {
|
||||
return func(rw http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != method {
|
||||
http.Error(rw, fmt.Sprintf("unsupported method: %s", req.Method), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
func mockBuilder() *servermock.Builder[*Client] {
|
||||
return servermock.NewBuilder[*Client](
|
||||
func(server *httptest.Server) (*Client, error) {
|
||||
client := NewClient("bob", "user", "secret")
|
||||
client.HTTPClient = server.Client()
|
||||
client.baseURL, _ = url.Parse(server.URL)
|
||||
|
||||
token := req.Header.Get(authTokenHeader)
|
||||
if token != "tok" {
|
||||
http.Error(rw, fmt.Sprintf("invalid credentials: %q", token), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
if file == "" {
|
||||
rw.WriteHeader(status)
|
||||
return
|
||||
}
|
||||
|
||||
open, err := os.Open(filepath.Join("fixtures", file))
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
defer func() { _ = open.Close() }()
|
||||
|
||||
rw.WriteHeader(status)
|
||||
_, err = io.Copy(rw, open)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func unauthenticatedHandler(method string, status int, file string) http.HandlerFunc {
|
||||
return func(rw http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != method {
|
||||
http.Error(rw, fmt.Sprintf("unsupported method: %s", req.Method), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
token := req.Header.Get(authTokenHeader)
|
||||
if token != "" {
|
||||
http.Error(rw, fmt.Sprintf("invalid credentials: %q", token), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
if file == "" {
|
||||
rw.WriteHeader(status)
|
||||
return
|
||||
}
|
||||
|
||||
open, err := os.Open(filepath.Join("fixtures", file))
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
defer func() { _ = open.Close() }()
|
||||
|
||||
rw.WriteHeader(status)
|
||||
_, err = io.Copy(rw, open)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
return client, nil
|
||||
},
|
||||
servermock.CheckHeader().WithJSONHeaders())
|
||||
}
|
||||
|
||||
func TestClient_Publish(t *testing.T) {
|
||||
client := setupTest(t, "/Zone/example.com", unauthenticatedHandler(http.MethodPut, http.StatusOK, "publish.json"))
|
||||
client := mockBuilder().
|
||||
Route("PUT /Zone/example.com", servermock.ResponseFromFixture("publish.json"),
|
||||
servermock.CheckRequestJSONBody(`{"publish":true,"notes":"my message"}`)).
|
||||
Build(t)
|
||||
|
||||
err := client.Publish(t.Context(), "example.com", "my message")
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestClient_AddTXTRecord(t *testing.T) {
|
||||
client := setupTest(t, "/TXTRecord/example.com/example.com.", unauthenticatedHandler(http.MethodPost, http.StatusCreated, "create-txt-record.json"))
|
||||
client := mockBuilder().
|
||||
Route("POST /TXTRecord/example.com/example.com.", servermock.ResponseFromFixture("create-txt-record.json"),
|
||||
servermock.CheckRequestJSONBody(`{"rdata":{"txtdata":"txt"},"ttl":"120"}`)).
|
||||
Build(t)
|
||||
|
||||
err := client.AddTXTRecord(t.Context(), "example.com", "example.com.", "txt", 120)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestClient_RemoveTXTRecord(t *testing.T) {
|
||||
client := setupTest(t, "/TXTRecord/example.com/example.com.", unauthenticatedHandler(http.MethodDelete, http.StatusOK, ""))
|
||||
client := mockBuilder().
|
||||
Route("DELETE /TXTRecord/example.com/example.com.", nil).
|
||||
Build(t)
|
||||
|
||||
err := client.RemoveTXTRecord(t.Context(), "example.com", "example.com.")
|
||||
require.NoError(t, err)
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@ package internal
|
|||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/go-acme/lego/v4/platform/tester/servermock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
|
@ -16,7 +16,10 @@ func mockContext(t *testing.T) context.Context {
|
|||
}
|
||||
|
||||
func TestClient_login(t *testing.T) {
|
||||
client := setupTest(t, "/Session", unauthenticatedHandler(http.MethodPost, http.StatusOK, "login.json"))
|
||||
client := mockBuilder().
|
||||
Route("POST /Session", servermock.ResponseFromFixture("login.json"),
|
||||
servermock.CheckRequestJSONBody(`{"customer_name":"bob","user_name":"user","password":"secret"}`)).
|
||||
Build(t)
|
||||
|
||||
sess, err := client.login(t.Context())
|
||||
require.NoError(t, err)
|
||||
|
|
@ -27,14 +30,22 @@ func TestClient_login(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_Logout(t *testing.T) {
|
||||
client := setupTest(t, "/Session", authenticatedHandler(http.MethodDelete, http.StatusOK, ""))
|
||||
client := servermock.NewBuilder[*Client](setupClient,
|
||||
servermock.CheckHeader().WithJSONHeaders().
|
||||
With(authTokenHeader, "tok"),
|
||||
).
|
||||
Route("DELETE /Session", nil).
|
||||
Build(t)
|
||||
|
||||
err := client.Logout(mockContext(t))
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestClient_CreateAuthenticatedContext(t *testing.T) {
|
||||
client := setupTest(t, "/Session", unauthenticatedHandler(http.MethodPost, http.StatusOK, "login.json"))
|
||||
client := mockBuilder().
|
||||
Route("POST /Session", servermock.ResponseFromFixture("login.json"),
|
||||
servermock.CheckRequestJSONBody(`{"customer_name":"bob","user_name":"user","password":"secret"}`)).
|
||||
Build(t)
|
||||
|
||||
ctx, err := client.CreateAuthenticatedContext(t.Context())
|
||||
require.NoError(t, err)
|
||||
|
|
|
|||
|
|
@ -1,56 +1,44 @@
|
|||
package internal
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/go-acme/lego/v4/platform/tester/servermock"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func setupTest(t *testing.T, message string) *Client {
|
||||
t.Helper()
|
||||
|
||||
mux := http.NewServeMux()
|
||||
server := httptest.NewServer(mux)
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
func setupClient(server *httptest.Server) (*Client, error) {
|
||||
client, err := NewClient("user", "secret")
|
||||
require.NoError(t, err)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client.HTTPClient = server.Client()
|
||||
client.baseURL = server.URL
|
||||
client.HTTPClient = server.Client()
|
||||
|
||||
mux.HandleFunc("GET /", func(rw http.ResponseWriter, req *http.Request) {
|
||||
query := req.URL.Query()
|
||||
|
||||
username := query.Get("username")
|
||||
if username != "user" {
|
||||
http.Error(rw, "invalid username: "+username, http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
password := query.Get("password")
|
||||
if password != "secret" {
|
||||
http.Error(rw, "invalid password: "+password, http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
_, _ = rw.Write([]byte(message))
|
||||
})
|
||||
|
||||
return client
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func TestAddTXTRecord(t *testing.T) {
|
||||
client := setupTest(t, "success")
|
||||
client := servermock.NewBuilder[*Client](setupClient).
|
||||
Route("GET /", servermock.RawStringResponse("success"),
|
||||
servermock.CheckQueryParameter().Strict().
|
||||
With("add_hostname", "sub.example.com").
|
||||
With("hostname", "example.com").
|
||||
With("password", "secret").
|
||||
With("txt", "value").
|
||||
With("username", "user")).
|
||||
Build(t)
|
||||
|
||||
err := client.AddTXTRecord(t.Context(), "example.com", "sub.example.com", "value")
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestAddTXTRecord_error(t *testing.T) {
|
||||
client := setupTest(t, "error: authentification failed")
|
||||
client := servermock.NewBuilder[*Client](setupClient).
|
||||
Route("GET /", servermock.RawStringResponse("error: authentification failed")).
|
||||
Build(t)
|
||||
|
||||
err := client.AddTXTRecord(t.Context(), "example.com", "sub.example.com", "value")
|
||||
require.EqualError(t, err, "error: authentification failed")
|
||||
|
|
|
|||
|
|
@ -1,52 +1,27 @@
|
|||
package internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/go-acme/lego/v4/platform/tester/servermock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func setupTest(t *testing.T, method, pattern string, status int, file string) *Client {
|
||||
t.Helper()
|
||||
func mockBuilder() *servermock.Builder[*Client] {
|
||||
return servermock.NewBuilder[*Client](
|
||||
func(server *httptest.Server) (*Client, error) {
|
||||
client := NewClient()
|
||||
client.HTTPClient = server.Client()
|
||||
client.baseURL, _ = url.Parse(server.URL)
|
||||
|
||||
mux := http.NewServeMux()
|
||||
server := httptest.NewServer(mux)
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
mux.HandleFunc(pattern, func(rw http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != method {
|
||||
http.Error(rw, fmt.Sprintf("unsupported method %s", req.Method), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
open, err := os.Open(file)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
defer func() { _ = open.Close() }()
|
||||
|
||||
rw.WriteHeader(status)
|
||||
_, err = io.Copy(rw, open)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
client := NewClient()
|
||||
client.HTTPClient = server.Client()
|
||||
client.baseURL, _ = url.Parse(server.URL)
|
||||
|
||||
return client
|
||||
return client, nil
|
||||
},
|
||||
servermock.CheckHeader().WithJSONHeaders(),
|
||||
)
|
||||
}
|
||||
|
||||
func TestGetRootDomain(t *testing.T) {
|
||||
|
|
@ -64,9 +39,9 @@ func TestGetRootDomain(t *testing.T) {
|
|||
}{
|
||||
{
|
||||
desc: "success",
|
||||
pattern: "/dns/getroot/test.lego.freeddns.org",
|
||||
pattern: "GET /dns/getroot/test.lego.freeddns.org",
|
||||
status: http.StatusOK,
|
||||
file: "./fixtures/get_root_domain.json",
|
||||
file: "get_root_domain.json",
|
||||
expected: expected{
|
||||
domain: &DNSHostname{
|
||||
APIException: &APIException{
|
||||
|
|
@ -81,9 +56,9 @@ func TestGetRootDomain(t *testing.T) {
|
|||
},
|
||||
{
|
||||
desc: "invalid",
|
||||
pattern: "/dns/getroot/test.lego.freeddns.org",
|
||||
pattern: "GET /dns/getroot/test.lego.freeddns.org",
|
||||
status: http.StatusNotImplemented,
|
||||
file: "./fixtures/get_root_domain_invalid.json",
|
||||
file: "get_root_domain_invalid.json",
|
||||
expected: expected{
|
||||
error: "API error: 501: Argument Exception: Invalid.",
|
||||
},
|
||||
|
|
@ -94,7 +69,9 @@ func TestGetRootDomain(t *testing.T) {
|
|||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
client := setupTest(t, http.MethodGet, test.pattern, test.status, test.file)
|
||||
client := mockBuilder().
|
||||
Route(test.pattern, servermock.ResponseFromFixture(test.file).WithStatusCode(test.status)).
|
||||
Build(t)
|
||||
|
||||
domain, err := client.GetRootDomain(t.Context(), "test.lego.freeddns.org")
|
||||
|
||||
|
|
@ -126,9 +103,9 @@ func TestGetRecords(t *testing.T) {
|
|||
}{
|
||||
{
|
||||
desc: "success",
|
||||
pattern: "/dns/record/_acme-challenge.lego.freeddns.org",
|
||||
pattern: "GET /dns/record/_acme-challenge.lego.freeddns.org",
|
||||
status: http.StatusOK,
|
||||
file: "./fixtures/get_records.json",
|
||||
file: "get_records.json",
|
||||
expected: expected{
|
||||
records: []DNSRecord{
|
||||
{
|
||||
|
|
@ -160,18 +137,18 @@ func TestGetRecords(t *testing.T) {
|
|||
},
|
||||
{
|
||||
desc: "empty",
|
||||
pattern: "/dns/record/_acme-challenge.lego.freeddns.org",
|
||||
pattern: "GET /dns/record/_acme-challenge.lego.freeddns.org",
|
||||
status: http.StatusOK,
|
||||
file: "./fixtures/get_records_empty.json",
|
||||
file: "get_records_empty.json",
|
||||
expected: expected{
|
||||
records: []DNSRecord{},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "invalid",
|
||||
pattern: "/dns/record/_acme-challenge.lego.freeddns.org",
|
||||
pattern: "GET /dns/record/_acme-challenge.lego.freeddns.org",
|
||||
status: http.StatusNotImplemented,
|
||||
file: "./fixtures/get_records_invalid.json",
|
||||
file: "get_records_invalid.json",
|
||||
expected: expected{
|
||||
error: "API error: 501: Argument Exception: Invalid.",
|
||||
},
|
||||
|
|
@ -182,7 +159,11 @@ func TestGetRecords(t *testing.T) {
|
|||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
client := setupTest(t, http.MethodGet, test.pattern, test.status, test.file)
|
||||
client := mockBuilder().
|
||||
Route(test.pattern, servermock.ResponseFromFixture(test.file).WithStatusCode(test.status),
|
||||
servermock.CheckQueryParameter().Strict().
|
||||
With("recordType", "TXT")).
|
||||
Build(t)
|
||||
|
||||
records, err := client.GetRecords(t.Context(), "_acme-challenge.lego.freeddns.org", "TXT")
|
||||
|
||||
|
|
@ -213,15 +194,15 @@ func TestAddNewRecord(t *testing.T) {
|
|||
}{
|
||||
{
|
||||
desc: "success",
|
||||
pattern: "/dns/9007481/record",
|
||||
pattern: "POST /dns/9007481/record",
|
||||
status: http.StatusOK,
|
||||
file: "./fixtures/add_new_record.json",
|
||||
file: "add_new_record.json",
|
||||
},
|
||||
{
|
||||
desc: "invalid",
|
||||
pattern: "/dns/9007481/record",
|
||||
pattern: "POST /dns/9007481/record",
|
||||
status: http.StatusNotImplemented,
|
||||
file: "./fixtures/add_new_record_invalid.json",
|
||||
file: "add_new_record_invalid.json",
|
||||
expected: expected{
|
||||
error: "API error: 501: Argument Exception: Invalid.",
|
||||
},
|
||||
|
|
@ -232,7 +213,10 @@ func TestAddNewRecord(t *testing.T) {
|
|||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
client := setupTest(t, http.MethodPost, test.pattern, test.status, test.file)
|
||||
client := mockBuilder().
|
||||
Route(test.pattern, servermock.ResponseFromFixture(test.file).WithStatusCode(test.status),
|
||||
servermock.CheckRequestJSONBodyFromFile("add_new_record-request.json")).
|
||||
Build(t)
|
||||
|
||||
record := DNSRecord{
|
||||
Type: "TXT",
|
||||
|
|
@ -270,15 +254,15 @@ func TestDeleteRecord(t *testing.T) {
|
|||
}{
|
||||
{
|
||||
desc: "success",
|
||||
pattern: "/",
|
||||
pattern: "DELETE /",
|
||||
status: http.StatusOK,
|
||||
file: "./fixtures/delete_record.json",
|
||||
file: "delete_record.json",
|
||||
},
|
||||
{
|
||||
desc: "invalid",
|
||||
pattern: "/",
|
||||
pattern: "DELETE /",
|
||||
status: http.StatusNotImplemented,
|
||||
file: "./fixtures/delete_record_invalid.json",
|
||||
file: "delete_record_invalid.json",
|
||||
expected: expected{
|
||||
error: "API error: 501: Argument Exception: Invalid.",
|
||||
},
|
||||
|
|
@ -289,7 +273,9 @@ func TestDeleteRecord(t *testing.T) {
|
|||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
client := setupTest(t, http.MethodDelete, test.pattern, test.status, test.file)
|
||||
client := mockBuilder().
|
||||
Route(test.pattern, servermock.ResponseFromFixture(test.file).WithStatusCode(test.status)).
|
||||
Build(t)
|
||||
|
||||
err := client.DeleteRecord(t.Context(), 9007481, 6041418)
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"recordType": "TXT",
|
||||
"domainName": "lego.freeddns.org",
|
||||
"nodeName": "_acme-challenge",
|
||||
"hostname": "_acme-challenge.lego.freeddns.org",
|
||||
"state": true,
|
||||
"textData": "txt_txt_txt_txt_txt_txt_txt_2",
|
||||
"ttl": 300
|
||||
}
|
||||
|
|
@ -2,7 +2,6 @@ package easydns
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
|
|
@ -10,12 +9,10 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/platform/tester"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/go-acme/lego/v4/platform/tester/servermock"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
const authorizationHeader = "Authorization"
|
||||
|
||||
const envDomain = envNamespace + "DOMAIN"
|
||||
|
||||
var envTest = tester.NewEnvTest(
|
||||
|
|
@ -24,26 +21,27 @@ var envTest = tester.NewEnvTest(
|
|||
EnvKey).
|
||||
WithDomain(envDomain)
|
||||
|
||||
func setupTest(t *testing.T) (*DNSProvider, *http.ServeMux) {
|
||||
t.Helper()
|
||||
func mockBuilder() *servermock.Builder[*DNSProvider] {
|
||||
return servermock.NewBuilder(
|
||||
func(server *httptest.Server) (*DNSProvider, error) {
|
||||
endpoint, err := url.Parse(server.URL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mux := http.NewServeMux()
|
||||
server := httptest.NewServer(mux)
|
||||
t.Cleanup(server.Close)
|
||||
config := NewDefaultConfig()
|
||||
config.Token = "TOKEN"
|
||||
config.Key = "SECRET"
|
||||
config.Endpoint = endpoint
|
||||
config.HTTPClient = server.Client()
|
||||
|
||||
endpoint, err := url.Parse(server.URL)
|
||||
require.NoError(t, err)
|
||||
|
||||
config := NewDefaultConfig()
|
||||
config.Token = "TOKEN"
|
||||
config.Key = "SECRET"
|
||||
config.Endpoint = endpoint
|
||||
config.HTTPClient = server.Client()
|
||||
|
||||
provider, err := NewDNSProviderConfig(config)
|
||||
require.NoError(t, err)
|
||||
|
||||
return provider, mux
|
||||
return NewDNSProviderConfig(config)
|
||||
},
|
||||
servermock.CheckHeader().
|
||||
WithJSONHeaders().
|
||||
WithAuthorization("Basic VE9LRU46U0VDUkVU"),
|
||||
servermock.CheckQueryParameter().Strict().
|
||||
With("format", "json"))
|
||||
}
|
||||
|
||||
func TestNewDNSProvider(t *testing.T) {
|
||||
|
|
@ -145,78 +143,50 @@ func TestNewDNSProviderConfig(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestDNSProvider_Present(t *testing.T) {
|
||||
provider, mux := setupTest(t)
|
||||
|
||||
mux.HandleFunc("/zones/records/all/example.com", func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Equal(t, http.MethodGet, r.Method, "method")
|
||||
assert.Equal(t, "format=json", r.URL.RawQuery, "query")
|
||||
assert.Equal(t, "Basic VE9LRU46U0VDUkVU", r.Header.Get(authorizationHeader), authorizationHeader)
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, err := fmt.Fprintf(w, `{
|
||||
"msg": "string",
|
||||
"status": 200,
|
||||
"tm": 0,
|
||||
"data": [{
|
||||
"id": "60898922",
|
||||
"domain": "example.com",
|
||||
"host": "hosta",
|
||||
"ttl": "300",
|
||||
"prio": "0",
|
||||
"geozone_id": "0",
|
||||
"type": "A",
|
||||
"rdata": "1.2.3.4",
|
||||
"last_mod": "2019-08-28 19:09:50"
|
||||
}],
|
||||
"count": 0,
|
||||
"total": 0,
|
||||
"start": 0,
|
||||
"max": 0
|
||||
}
|
||||
`)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
provider := mockBuilder().
|
||||
Route("GET /zones/records/all/example.com",
|
||||
servermock.RawStringResponse(`{
|
||||
"msg": "string",
|
||||
"status": 200,
|
||||
"tm": 0,
|
||||
"data": [{
|
||||
"id": "60898922",
|
||||
"domain": "example.com",
|
||||
"host": "hosta",
|
||||
"ttl": "300",
|
||||
"prio": "0",
|
||||
"geozone_id": "0",
|
||||
"type": "A",
|
||||
"rdata": "1.2.3.4",
|
||||
"last_mod": "2019-08-28 19:09:50"
|
||||
}],
|
||||
"count": 0,
|
||||
"total": 0,
|
||||
"start": 0,
|
||||
"max": 0
|
||||
}
|
||||
})
|
||||
|
||||
mux.HandleFunc("/zones/records/add/example.com/TXT", func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Equal(t, http.MethodPut, r.Method, "method")
|
||||
assert.Equal(t, "format=json", r.URL.RawQuery, "query")
|
||||
assert.Equal(t, "application/json", r.Header.Get("Content-Type"), "Content-Type")
|
||||
assert.Equal(t, "Basic VE9LRU46U0VDUkVU", r.Header.Get(authorizationHeader), authorizationHeader)
|
||||
|
||||
reqBody, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
expectedReqBody := `{"domain":"example.com","host":"_acme-challenge","ttl":"120","prio":"0","type":"TXT","rdata":"pW9ZKG0xz_PCriK-nCMOjADy9eJcgGWIzkkj2fN4uZM"}
|
||||
`
|
||||
assert.Equal(t, expectedReqBody, string(reqBody))
|
||||
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
_, err = fmt.Fprintf(w, `{
|
||||
"msg": "OK",
|
||||
"tm": 1554681934,
|
||||
"data": {
|
||||
"host": "_acme-challenge",
|
||||
"geozone_id": 0,
|
||||
"ttl": "120",
|
||||
"prio": "0",
|
||||
"rdata": "pW9ZKG0xz_PCriK-nCMOjADy9eJcgGWIzkkj2fN4uZM",
|
||||
"revoked": 0,
|
||||
"id": "123456789",
|
||||
"new_host": "_acme-challenge.example.com"
|
||||
},
|
||||
"status": 201
|
||||
}`)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
})
|
||||
`),
|
||||
servermock.CheckQueryParameter().Strict().
|
||||
With("format", "json")).
|
||||
Route("PUT /zones/records/add/example.com/TXT",
|
||||
servermock.RawStringResponse(`{
|
||||
"msg": "OK",
|
||||
"tm": 1554681934,
|
||||
"data": {
|
||||
"host": "_acme-challenge",
|
||||
"geozone_id": 0,
|
||||
"ttl": "120",
|
||||
"prio": "0",
|
||||
"rdata": "pW9ZKG0xz_PCriK-nCMOjADy9eJcgGWIzkkj2fN4uZM",
|
||||
"revoked": 0,
|
||||
"id": "123456789",
|
||||
"new_host": "_acme-challenge.example.com"
|
||||
},
|
||||
"status": 201
|
||||
}`),
|
||||
servermock.CheckRequestJSONBody(`{"domain":"example.com","host":"_acme-challenge","ttl":"120","prio":"0","type":"TXT","rdata":"pW9ZKG0xz_PCriK-nCMOjADy9eJcgGWIzkkj2fN4uZM"}
|
||||
`)).
|
||||
Build(t)
|
||||
|
||||
err := provider.Present("example.com", "token", "keyAuth")
|
||||
require.NoError(t, err)
|
||||
|
|
@ -224,163 +194,116 @@ func TestDNSProvider_Present(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestDNSProvider_Cleanup_WhenRecordIdNotSet_NoOp(t *testing.T) {
|
||||
provider, mux := setupTest(t)
|
||||
|
||||
mux.HandleFunc("/zones/records/all/example.com", func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Equal(t, http.MethodGet, r.Method, "method")
|
||||
assert.Equal(t, "format=json", r.URL.RawQuery, "query")
|
||||
assert.Equal(t, "Basic VE9LRU46U0VDUkVU", r.Header.Get(authorizationHeader), authorizationHeader)
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, err := fmt.Fprintf(w, `{
|
||||
"msg": "string",
|
||||
"status": 200,
|
||||
"tm": 0,
|
||||
"data": [{
|
||||
"id": "60898922",
|
||||
"domain": "example.com",
|
||||
"host": "hosta",
|
||||
"ttl": "300",
|
||||
"prio": "0",
|
||||
"geozone_id": "0",
|
||||
"type": "A",
|
||||
"rdata": "1.2.3.4",
|
||||
"last_mod": "2019-08-28 19:09:50"
|
||||
}],
|
||||
"count": 0,
|
||||
"total": 0,
|
||||
"start": 0,
|
||||
"max": 0
|
||||
}
|
||||
`)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
})
|
||||
provider := mockBuilder().
|
||||
Route("GET /zones/records/all/_acme-challenge.example.com",
|
||||
servermock.RawStringResponse(`{
|
||||
"msg": "string",
|
||||
"status": 200,
|
||||
"tm": 0,
|
||||
"data": [{
|
||||
"id": "60898922",
|
||||
"domain": "example.com",
|
||||
"host": "hosta",
|
||||
"ttl": "300",
|
||||
"prio": "0",
|
||||
"geozone_id": "0",
|
||||
"type": "A",
|
||||
"rdata": "1.2.3.4",
|
||||
"last_mod": "2019-08-28 19:09:50"
|
||||
}],
|
||||
"count": 0,
|
||||
"total": 0,
|
||||
"start": 0,
|
||||
"max": 0
|
||||
}
|
||||
`)).
|
||||
Build(t)
|
||||
|
||||
err := provider.CleanUp("example.com", "token", "keyAuth")
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestDNSProvider_Cleanup_WhenRecordIdSet_DeletesTxtRecord(t *testing.T) {
|
||||
provider, mux := setupTest(t)
|
||||
|
||||
mux.HandleFunc("/zones/records/all/example.com", func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Equal(t, http.MethodGet, r.Method, "method")
|
||||
assert.Equal(t, "format=json", r.URL.RawQuery, "query")
|
||||
assert.Equal(t, "Basic VE9LRU46U0VDUkVU", r.Header.Get(authorizationHeader), authorizationHeader)
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, err := fmt.Fprintf(w, `{
|
||||
"msg": "string",
|
||||
"status": 200,
|
||||
"tm": 0,
|
||||
"data": [{
|
||||
"id": "60898922",
|
||||
"domain": "example.com",
|
||||
"host": "hosta",
|
||||
"ttl": "300",
|
||||
"prio": "0",
|
||||
"geozone_id": "0",
|
||||
"type": "A",
|
||||
"rdata": "1.2.3.4",
|
||||
"last_mod": "2019-08-28 19:09:50"
|
||||
}],
|
||||
"count": 0,
|
||||
"total": 0,
|
||||
"start": 0,
|
||||
"max": 0
|
||||
}
|
||||
`)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
mux.HandleFunc("/zones/records/example.com/123456", func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Equal(t, http.MethodDelete, r.Method, "method")
|
||||
assert.Equal(t, "format=json", r.URL.RawQuery, "query")
|
||||
assert.Equal(t, "Basic VE9LRU46U0VDUkVU", r.Header.Get(authorizationHeader), authorizationHeader)
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, err := fmt.Fprintf(w, `{
|
||||
"msg": "OK",
|
||||
"data": {
|
||||
"domain": "example.com",
|
||||
"id": "123456"
|
||||
},
|
||||
"status": 200
|
||||
}`)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
})
|
||||
provider := mockBuilder().
|
||||
Route("GET /zones/records/all/_acme-challenge.example.com",
|
||||
servermock.RawStringResponse(`{
|
||||
"msg": "string",
|
||||
"status": 200,
|
||||
"tm": 0,
|
||||
"data": [{
|
||||
"id": "60898922",
|
||||
"domain": "example.com",
|
||||
"host": "hosta",
|
||||
"ttl": "300",
|
||||
"prio": "0",
|
||||
"geozone_id": "0",
|
||||
"type": "A",
|
||||
"rdata": "1.2.3.4",
|
||||
"last_mod": "2019-08-28 19:09:50"
|
||||
}],
|
||||
"count": 0,
|
||||
"total": 0,
|
||||
"start": 0,
|
||||
"max": 0
|
||||
}
|
||||
`)).
|
||||
Route("DELETE /zones/records/_acme-challenge.example.com/123456",
|
||||
servermock.RawStringResponse(`{
|
||||
"msg": "OK",
|
||||
"data": {
|
||||
"domain": "example.com",
|
||||
"id": "123456"
|
||||
},
|
||||
"status": 200
|
||||
}`)).
|
||||
Build(t)
|
||||
|
||||
provider.recordIDs["_acme-challenge.example.com.|pW9ZKG0xz_PCriK-nCMOjADy9eJcgGWIzkkj2fN4uZM"] = "123456"
|
||||
|
||||
err := provider.CleanUp("example.com", "token", "keyAuth")
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestDNSProvider_Cleanup_WhenHttpError_ReturnsError(t *testing.T) {
|
||||
provider, mux := setupTest(t)
|
||||
|
||||
mux.HandleFunc("/zones/records/all/example.com", func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Equal(t, http.MethodGet, r.Method, "method")
|
||||
assert.Equal(t, "format=json", r.URL.RawQuery, "query")
|
||||
assert.Equal(t, "Basic VE9LRU46U0VDUkVU", r.Header.Get(authorizationHeader), authorizationHeader)
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, err := fmt.Fprintf(w, `{
|
||||
"msg": "string",
|
||||
"status": 200,
|
||||
"tm": 0,
|
||||
"data": [{
|
||||
"id": "60898922",
|
||||
"domain": "example.com",
|
||||
"host": "hosta",
|
||||
"ttl": "300",
|
||||
"prio": "0",
|
||||
"geozone_id": "0",
|
||||
"type": "A",
|
||||
"rdata": "1.2.3.4",
|
||||
"last_mod": "2019-08-28 19:09:50"
|
||||
}],
|
||||
"count": 0,
|
||||
"total": 0,
|
||||
"start": 0,
|
||||
"max": 0
|
||||
}
|
||||
`)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
errorMessage := `{
|
||||
"error": {
|
||||
"code": 406,
|
||||
"message": "Provided id is invalid or you do not have permission to access it."
|
||||
}
|
||||
}`
|
||||
mux.HandleFunc("/zones/records/example.com/123456", func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Equal(t, http.MethodDelete, r.Method, "method")
|
||||
assert.Equal(t, "format=json", r.URL.RawQuery, "query")
|
||||
assert.Equal(t, "Basic VE9LRU46U0VDUkVU", r.Header.Get(authorizationHeader), authorizationHeader)
|
||||
|
||||
w.WriteHeader(http.StatusNotAcceptable)
|
||||
_, err := fmt.Fprint(w, errorMessage)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
})
|
||||
provider := mockBuilder().
|
||||
Route("GET /zones/records/all/example.com",
|
||||
servermock.RawStringResponse(`{
|
||||
"msg": "string",
|
||||
"status": 200,
|
||||
"tm": 0,
|
||||
"data": [{
|
||||
"id": "60898922",
|
||||
"domain": "example.com",
|
||||
"host": "hosta",
|
||||
"ttl": "300",
|
||||
"prio": "0",
|
||||
"geozone_id": "0",
|
||||
"type": "A",
|
||||
"rdata": "1.2.3.4",
|
||||
"last_mod": "2019-08-28 19:09:50"
|
||||
}],
|
||||
"count": 0,
|
||||
"total": 0,
|
||||
"start": 0,
|
||||
"max": 0
|
||||
}
|
||||
`)).
|
||||
Route("DELETE /zones/records/example.com/123456",
|
||||
servermock.RawStringResponse(errorMessage).
|
||||
WithStatusCode(http.StatusNotAcceptable)).
|
||||
Build(t)
|
||||
|
||||
provider.recordIDs["_acme-challenge.example.com.|pW9ZKG0xz_PCriK-nCMOjADy9eJcgGWIzkkj2fN4uZM"] = "123456"
|
||||
|
||||
err := provider.CleanUp("example.com", "token", "keyAuth")
|
||||
|
||||
expectedError := fmt.Sprintf("easydns: unexpected status code: [status code: 406] body: %v", errorMessage)
|
||||
require.EqualError(t, err, expectedError)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,73 +1,34 @@
|
|||
package internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/go-acme/lego/v4/platform/tester/servermock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func setupTest(t *testing.T, method, pattern string, status int, file string) *Client {
|
||||
t.Helper()
|
||||
func mockBuilder() *servermock.Builder[*Client] {
|
||||
return servermock.NewBuilder[*Client](
|
||||
func(server *httptest.Server) (*Client, error) {
|
||||
client := NewClient("tok", "k")
|
||||
client.HTTPClient = server.Client()
|
||||
client.BaseURL, _ = url.Parse(server.URL)
|
||||
|
||||
mux := http.NewServeMux()
|
||||
server := httptest.NewServer(mux)
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
mux.HandleFunc(pattern, func(rw http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != method {
|
||||
http.Error(rw, fmt.Sprintf("unsupported method: %s", req.Method), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
token, key, ok := req.BasicAuth()
|
||||
if token != "tok" || key != "k" || !ok {
|
||||
http.Error(rw, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
if req.URL.Query().Get("format") != "json" {
|
||||
http.Error(rw, fmt.Sprintf("invalid format: %s", req.URL.Query().Get("format")), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if file == "" {
|
||||
rw.WriteHeader(status)
|
||||
return
|
||||
}
|
||||
|
||||
open, err := os.Open(filepath.Join("fixtures", file))
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
defer func() { _ = open.Close() }()
|
||||
|
||||
rw.WriteHeader(status)
|
||||
_, err = io.Copy(rw, open)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
client := NewClient("tok", "k")
|
||||
client.HTTPClient = server.Client()
|
||||
client.BaseURL, _ = url.Parse(server.URL)
|
||||
|
||||
return client
|
||||
return client, nil
|
||||
},
|
||||
servermock.CheckHeader().WithJSONHeaders().
|
||||
WithBasicAuth("tok", "k"),
|
||||
)
|
||||
}
|
||||
|
||||
func TestClient_ListZones(t *testing.T) {
|
||||
client := setupTest(t, http.MethodGet, "/zones/records/all/example.com", http.StatusOK, "list-zone.json")
|
||||
client := mockBuilder().
|
||||
Route("GET /zones/records/all/example.com", servermock.ResponseFromFixture("list-zone.json")).
|
||||
Build(t)
|
||||
|
||||
zones, err := client.ListZones(t.Context(), "example.com")
|
||||
require.NoError(t, err)
|
||||
|
|
@ -87,14 +48,20 @@ func TestClient_ListZones(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_ListZones_error(t *testing.T) {
|
||||
client := setupTest(t, http.MethodGet, "/zones/records/all/example.com", http.StatusOK, "error1.json")
|
||||
client := mockBuilder().
|
||||
Route("GET /zones/records/all/example.com", servermock.ResponseFromFixture("error1.json")).
|
||||
Build(t)
|
||||
|
||||
_, err := client.ListZones(t.Context(), "example.com")
|
||||
require.EqualError(t, err, "code 420: Enhance Your Calm. Rate limit exceeded (too many requests) OR you did NOT provide any credentials with your request!")
|
||||
}
|
||||
|
||||
func TestClient_AddRecord(t *testing.T) {
|
||||
client := setupTest(t, http.MethodPut, "/zones/records/add/example.com/TXT", http.StatusCreated, "add-record.json")
|
||||
client := mockBuilder().
|
||||
Route("PUT /zones/records/add/example.com/TXT",
|
||||
servermock.ResponseFromFixture("add-record.json").WithStatusCode(http.StatusCreated),
|
||||
servermock.CheckRequestJSONBody(`{"domain":"example.com","host":"test631","ttl":"300","prio":"0","type":"TXT","rdata":"txt"}`)).
|
||||
Build(t)
|
||||
|
||||
record := ZoneRecord{
|
||||
Domain: "example.com",
|
||||
|
|
@ -112,7 +79,10 @@ func TestClient_AddRecord(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_AddRecord_error(t *testing.T) {
|
||||
client := setupTest(t, http.MethodPut, "/zones/records/add/example.com/TXT", http.StatusCreated, "error1.json")
|
||||
client := mockBuilder().
|
||||
Route("PUT /zones/records/add/example.com/TXT",
|
||||
servermock.ResponseFromFixture("error1.json").WithStatusCode(http.StatusCreated)).
|
||||
Build(t)
|
||||
|
||||
record := ZoneRecord{
|
||||
Domain: "example.com",
|
||||
|
|
@ -128,7 +98,9 @@ func TestClient_AddRecord_error(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_DeleteRecord(t *testing.T) {
|
||||
client := setupTest(t, http.MethodDelete, "/zones/records/example.com/xxx", http.StatusOK, "")
|
||||
client := mockBuilder().
|
||||
Route("DELETE /zones/records/example.com/xxx", nil).
|
||||
Build(t)
|
||||
|
||||
err := client.DeleteRecord(t.Context(), "example.com", "xxx")
|
||||
require.NoError(t, err)
|
||||
|
|
|
|||
|
|
@ -1,79 +1,38 @@
|
|||
package internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/go-acme/lego/v4/platform/tester/servermock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func setupTest(t *testing.T, method, pattern string, status int, file string) *Client {
|
||||
t.Helper()
|
||||
func mockBuilder() *servermock.Builder[*Client] {
|
||||
return servermock.NewBuilder[*Client](
|
||||
func(server *httptest.Server) (*Client, error) {
|
||||
srvURL, _ := url.Parse(server.URL)
|
||||
|
||||
mux := http.NewServeMux()
|
||||
server := httptest.NewServer(mux)
|
||||
t.Cleanup(server.Close)
|
||||
client := NewClient(srvURL.Host, "user", "secret")
|
||||
client.HTTPClient = server.Client()
|
||||
client.baseURL, _ = url.Parse(server.URL)
|
||||
|
||||
mux.HandleFunc(pattern, func(rw http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != method {
|
||||
http.Error(rw, fmt.Sprintf("unsupported method %s", req.Method), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
username, password, ok := req.BasicAuth()
|
||||
if !ok {
|
||||
http.Error(rw, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
if username != "user" {
|
||||
http.Error(rw, fmt.Sprintf("username: want %s got %s", username, "user"), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
if password != "secret" {
|
||||
http.Error(rw, fmt.Sprintf("password: want %s got %s", password, "secret"), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
open, err := os.Open(filepath.Join("fixtures", file))
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
defer func() { _ = open.Close() }()
|
||||
|
||||
rw.WriteHeader(status)
|
||||
_, err = io.Copy(rw, open)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
srvURL, _ := url.Parse(server.URL)
|
||||
|
||||
client := NewClient(srvURL.Host, "user", "secret")
|
||||
client.HTTPClient = server.Client()
|
||||
client.baseURL, _ = url.Parse(server.URL)
|
||||
|
||||
return client
|
||||
return client, nil
|
||||
},
|
||||
servermock.CheckHeader().WithJSONHeaders().
|
||||
WithBasicAuth("user", "secret"),
|
||||
)
|
||||
}
|
||||
|
||||
func TestListRecords(t *testing.T) {
|
||||
client := setupTest(t, http.MethodGet, "/dns_rr_list", http.StatusOK, "dns_rr_list.json")
|
||||
client := mockBuilder().
|
||||
Route("GET /dns_rr_list", servermock.ResponseFromFixture("dns_rr_list.json")).
|
||||
Build(t)
|
||||
|
||||
ctx := t.Context()
|
||||
|
||||
records, err := client.ListRecords(ctx)
|
||||
records, err := client.ListRecords(t.Context())
|
||||
require.NoError(t, err)
|
||||
|
||||
expected := []ResourceRecord{
|
||||
|
|
@ -336,11 +295,13 @@ func TestListRecords(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestGetRecord(t *testing.T) {
|
||||
client := setupTest(t, http.MethodGet, "/dns_rr_info", http.StatusOK, "dns_rr_info.json")
|
||||
client := mockBuilder().
|
||||
Route("GET /dns_rr_info", servermock.ResponseFromFixture("dns_rr_info.json"),
|
||||
servermock.CheckQueryParameter().Strict().
|
||||
With("rr_id", "239")).
|
||||
Build(t)
|
||||
|
||||
ctx := t.Context()
|
||||
|
||||
record, err := client.GetRecord(ctx, "239")
|
||||
record, err := client.GetRecord(t.Context(), "239")
|
||||
require.NoError(t, err)
|
||||
|
||||
expected := &ResourceRecord{
|
||||
|
|
@ -383,9 +344,11 @@ func TestGetRecord(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestAddRecord(t *testing.T) {
|
||||
client := setupTest(t, http.MethodPost, "/dns_rr_add", http.StatusCreated, "dns_rr_add.json")
|
||||
|
||||
ctx := t.Context()
|
||||
client := mockBuilder().
|
||||
Route("POST /dns_rr_add",
|
||||
servermock.ResponseFromFixture("dns_rr_add.json").WithStatusCode(http.StatusCreated),
|
||||
servermock.CheckRequestJSONBody(`{"dns_name":"dns.smart","dnsview_name":"external","rr_name":"test.example.com","rr_type":"TXT","value1":"test"}`)).
|
||||
Build(t)
|
||||
|
||||
r := ResourceRecord{
|
||||
RRName: "test.example.com",
|
||||
|
|
@ -395,7 +358,7 @@ func TestAddRecord(t *testing.T) {
|
|||
DNSViewName: "external",
|
||||
}
|
||||
|
||||
resp, err := client.AddRecord(ctx, r)
|
||||
resp, err := client.AddRecord(t.Context(), r)
|
||||
require.NoError(t, err)
|
||||
|
||||
expected := &BaseOutput{RetOID: "239"}
|
||||
|
|
@ -404,11 +367,13 @@ func TestAddRecord(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestDeleteRecord(t *testing.T) {
|
||||
client := setupTest(t, http.MethodDelete, "/dns_rr_delete", http.StatusOK, "dns_rr_delete.json")
|
||||
client := mockBuilder().
|
||||
Route("DELETE /dns_rr_delete", servermock.ResponseFromFixture("dns_rr_delete.json"),
|
||||
servermock.CheckQueryParameter().Strict().
|
||||
With("rr_id", "251")).
|
||||
Build(t)
|
||||
|
||||
ctx := t.Context()
|
||||
|
||||
resp, err := client.DeleteRecord(ctx, DeleteInputParameters{RRID: "251"})
|
||||
resp, err := client.DeleteRecord(t.Context(), DeleteInputParameters{RRID: "251"})
|
||||
require.NoError(t, err)
|
||||
|
||||
expected := &BaseOutput{RetOID: "251"}
|
||||
|
|
@ -417,10 +382,11 @@ func TestDeleteRecord(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestDeleteRecord_error(t *testing.T) {
|
||||
client := setupTest(t, http.MethodDelete, "/dns_rr_delete", http.StatusBadRequest, "dns_rr_delete-error.json")
|
||||
client := mockBuilder().
|
||||
Route("DELETE /dns_rr_delete",
|
||||
servermock.ResponseFromFixture("dns_rr_delete-error.json").WithStatusCode(http.StatusBadRequest)).
|
||||
Build(t)
|
||||
|
||||
ctx := t.Context()
|
||||
|
||||
_, err := client.DeleteRecord(ctx, DeleteInputParameters{RRID: "251"})
|
||||
_, err := client.DeleteRecord(t.Context(), DeleteInputParameters{RRID: "251"})
|
||||
require.ErrorAs(t, err, &APIError{})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,37 +1,36 @@
|
|||
package internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/go-acme/lego/v4/platform/tester/servermock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func setupTest(t *testing.T) (*Client, *http.ServeMux) {
|
||||
t.Helper()
|
||||
func mockBuilder() *servermock.Builder[*Client] {
|
||||
return servermock.NewBuilder[*Client](
|
||||
func(server *httptest.Server) (*Client, error) {
|
||||
client := NewClient("secret")
|
||||
client.HTTPClient = server.Client()
|
||||
client.baseURL, _ = url.Parse(server.URL)
|
||||
|
||||
mux := http.NewServeMux()
|
||||
server := httptest.NewServer(mux)
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
client := NewClient("secret")
|
||||
client.HTTPClient = server.Client()
|
||||
client.baseURL, _ = url.Parse(server.URL)
|
||||
|
||||
return client, mux
|
||||
return client, nil
|
||||
},
|
||||
servermock.CheckHeader().WithJSONHeaders(),
|
||||
)
|
||||
}
|
||||
|
||||
func TestClient_GetDNSRecords(t *testing.T) {
|
||||
client, mux := setupTest(t)
|
||||
|
||||
mux.HandleFunc("/domains/example.com/records", testHandler(http.MethodGet, http.StatusOK, "getDnsRecord.json"))
|
||||
client := mockBuilder().
|
||||
Route("GET /domains/example.com/records",
|
||||
servermock.ResponseFromFixture("getDnsRecord.json"),
|
||||
servermock.CheckQueryParameter().Strict().
|
||||
With("SIGNATURE", "secret")).
|
||||
Build(t)
|
||||
|
||||
records, err := client.GetDNSRecords(t.Context(), "example.com")
|
||||
require.NoError(t, err)
|
||||
|
|
@ -88,18 +87,25 @@ func TestClient_GetDNSRecords(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_GetDNSRecords_error(t *testing.T) {
|
||||
client, mux := setupTest(t)
|
||||
|
||||
mux.HandleFunc("/domains/example.com/records", testHandler(http.MethodGet, http.StatusUnauthorized, "error.json"))
|
||||
client := mockBuilder().
|
||||
Route("GET /domains/example.com/records",
|
||||
servermock.ResponseFromFixture("error.json").
|
||||
WithStatusCode(http.StatusUnauthorized),
|
||||
servermock.CheckQueryParameter().Strict().
|
||||
With("SIGNATURE", "secret")).
|
||||
Build(t)
|
||||
|
||||
_, err := client.GetDNSRecords(t.Context(), "example.com")
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestClient_CreateHostRecord(t *testing.T) {
|
||||
client, mux := setupTest(t)
|
||||
|
||||
mux.HandleFunc("/domains/example.com/records", testHandler(http.MethodPost, http.StatusOK, "createHostRecord.json"))
|
||||
client := mockBuilder().
|
||||
Route("POST /domains/example.com/records",
|
||||
servermock.ResponseFromFixture("createHostRecord.json"),
|
||||
servermock.CheckQueryParameter().Strict().
|
||||
With("SIGNATURE", "secret")).
|
||||
Build(t)
|
||||
|
||||
record := RecordRequest{
|
||||
Host: "www2",
|
||||
|
|
@ -121,9 +127,13 @@ func TestClient_CreateHostRecord(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_CreateHostRecord_error(t *testing.T) {
|
||||
client, mux := setupTest(t)
|
||||
|
||||
mux.HandleFunc("/domains/example.com/records", testHandler(http.MethodPost, http.StatusUnauthorized, "error.json"))
|
||||
client := mockBuilder().
|
||||
Route("POST /domains/example.com/records",
|
||||
servermock.ResponseFromFixture("error.json").
|
||||
WithStatusCode(http.StatusUnauthorized),
|
||||
servermock.CheckQueryParameter().Strict().
|
||||
With("SIGNATURE", "secret")).
|
||||
Build(t)
|
||||
|
||||
record := RecordRequest{
|
||||
Host: "www2",
|
||||
|
|
@ -138,9 +148,13 @@ func TestClient_CreateHostRecord_error(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_RemoveHostRecord(t *testing.T) {
|
||||
client, mux := setupTest(t)
|
||||
|
||||
mux.HandleFunc("/domains/example.com/records", testHandler(http.MethodDelete, http.StatusOK, "removeHostRecord.json"))
|
||||
client := mockBuilder().
|
||||
Route("DELETE /domains/example.com/records",
|
||||
servermock.ResponseFromFixture("removeHostRecord.json"),
|
||||
servermock.CheckQueryParameter().Strict().
|
||||
With("ID", "abc123").
|
||||
With("SIGNATURE", "secret")).
|
||||
Build(t)
|
||||
|
||||
data, err := client.RemoveHostRecord(t.Context(), "example.com", "abc123")
|
||||
require.NoError(t, err)
|
||||
|
|
@ -154,45 +168,12 @@ func TestClient_RemoveHostRecord(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_RemoveHostRecord_error(t *testing.T) {
|
||||
client, mux := setupTest(t)
|
||||
|
||||
mux.HandleFunc("/domains/example.com/records", testHandler(http.MethodDelete, http.StatusUnauthorized, "error.json"))
|
||||
client := mockBuilder().
|
||||
Route("DELETE /domains/example.com/records",
|
||||
servermock.ResponseFromFixture("error.json").
|
||||
WithStatusCode(http.StatusUnauthorized)).
|
||||
Build(t)
|
||||
|
||||
_, err := client.RemoveHostRecord(t.Context(), "example.com", "abc123")
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func testHandler(method string, statusCode int, filename string) http.HandlerFunc {
|
||||
return func(rw http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != method {
|
||||
http.Error(rw, fmt.Sprintf(`{"message":"unsupported method: %s"}`, req.Method), http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
auth := req.URL.Query().Get("SIGNATURE")
|
||||
if auth != "secret" {
|
||||
http.Error(rw, fmt.Sprintf("invalid API key: %s", auth), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
rw.WriteHeader(statusCode)
|
||||
|
||||
if statusCode == http.StatusNoContent {
|
||||
return
|
||||
}
|
||||
|
||||
file, err := os.Open(filepath.Join("fixtures", filename))
|
||||
if err != nil {
|
||||
http.Error(rw, fmt.Sprintf(`{"message":"%v"}`, err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
defer func() { _ = file.Close() }()
|
||||
|
||||
_, err = io.Copy(rw, file)
|
||||
if err != nil {
|
||||
http.Error(rw, fmt.Sprintf(`{"message":"%v"}`, err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,58 +1,39 @@
|
|||
package internal
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/go-acme/lego/v4/platform/tester/servermock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func setupTest(t *testing.T, pattern string, status int, filename string) *Client {
|
||||
t.Helper()
|
||||
func mockBuilder() *servermock.Builder[*Client] {
|
||||
return servermock.NewBuilder[*Client](
|
||||
func(server *httptest.Server) (*Client, error) {
|
||||
client, err := NewClient("secret", "shortname")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mux := http.NewServeMux()
|
||||
server := httptest.NewServer(mux)
|
||||
t.Cleanup(server.Close)
|
||||
client.HTTPClient = server.Client()
|
||||
client.baseURL, _ = url.Parse(server.URL)
|
||||
|
||||
mux.HandleFunc(pattern, func(rw http.ResponseWriter, req *http.Request) {
|
||||
if filename == "" {
|
||||
rw.WriteHeader(status)
|
||||
return
|
||||
}
|
||||
|
||||
file, err := os.Open(filepath.Join("fixtures", filename))
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
defer func() { _ = file.Close() }()
|
||||
|
||||
rw.WriteHeader(status)
|
||||
_, err = io.Copy(rw, file)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
client, err := NewClient("secret", "shortname")
|
||||
require.NoError(t, err)
|
||||
|
||||
client.HTTPClient = server.Client()
|
||||
client.baseURL, _ = url.Parse(server.URL)
|
||||
|
||||
return client
|
||||
return client, nil
|
||||
},
|
||||
servermock.CheckHeader().WithJSONHeaders().
|
||||
WithAuthorization("APIToken secret"))
|
||||
}
|
||||
|
||||
func TestClient_Create(t *testing.T) {
|
||||
client := setupTest(t, "POST /api/config/dns/namespaces/system/dns_zones/example.com/rrsets/groupA", http.StatusOK, "create.json")
|
||||
client := mockBuilder().
|
||||
Route("POST /api/config/dns/namespaces/system/dns_zones/example.com/rrsets/groupA",
|
||||
servermock.ResponseFromFixture("create.json"),
|
||||
servermock.CheckRequestJSONBody(`{"dns_zone_name":"example.com","group_name":"groupA","rrset":{"description":"lego","ttl":60,"txt_record":{"name":"wwww","values":["txt"]}}}`)).
|
||||
Build(t)
|
||||
|
||||
rrSet := RRSet{
|
||||
Description: "lego",
|
||||
|
|
@ -82,7 +63,10 @@ func TestClient_Create(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_Create_error(t *testing.T) {
|
||||
client := setupTest(t, "POST /api/config/dns/namespaces/system/dns_zones/example.com/rrsets/groupA", http.StatusBadRequest, "")
|
||||
client := mockBuilder().
|
||||
Route("POST /api/config/dns/namespaces/system/dns_zones/example.com/rrsets/groupA",
|
||||
servermock.Noop().WithStatusCode(http.StatusBadRequest)).
|
||||
Build(t)
|
||||
|
||||
rrSet := RRSet{
|
||||
Description: "lego",
|
||||
|
|
@ -98,7 +82,10 @@ func TestClient_Create_error(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_Get(t *testing.T) {
|
||||
client := setupTest(t, "GET /api/config/dns/namespaces/system/dns_zones/example.com/rrsets/groupA/www/TXT", http.StatusOK, "get.json")
|
||||
client := mockBuilder().
|
||||
Route("GET /api/config/dns/namespaces/system/dns_zones/example.com/rrsets/groupA/www/TXT",
|
||||
servermock.ResponseFromFixture("get.json")).
|
||||
Build(t)
|
||||
|
||||
result, err := client.GetRRSet(t.Context(), "example.com", "groupA", "www", "TXT")
|
||||
require.NoError(t, err)
|
||||
|
|
@ -122,7 +109,10 @@ func TestClient_Get(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_Get_not_found(t *testing.T) {
|
||||
client := setupTest(t, "GET /api/config/dns/namespaces/system/dns_zones/example.com/rrsets/groupA/www/TXT", http.StatusNotFound, "error_404.json")
|
||||
client := mockBuilder().
|
||||
Route("GET /api/config/dns/namespaces/system/dns_zones/example.com/rrsets/groupA/www/TXT",
|
||||
servermock.ResponseFromFixture("error_404.json").WithStatusCode(http.StatusNotFound)).
|
||||
Build(t)
|
||||
|
||||
result, err := client.GetRRSet(t.Context(), "example.com", "groupA", "www", "TXT")
|
||||
require.NoError(t, err)
|
||||
|
|
@ -131,14 +121,20 @@ func TestClient_Get_not_found(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_Get_error(t *testing.T) {
|
||||
client := setupTest(t, "GET /api/config/dns/namespaces/system/dns_zones/example.com/rrsets/groupA/www/TXT", http.StatusBadRequest, "")
|
||||
client := mockBuilder().
|
||||
Route("GET /api/config/dns/namespaces/system/dns_zones/example.com/rrsets/groupA/www/TXT",
|
||||
servermock.Noop().WithStatusCode(http.StatusBadRequest)).
|
||||
Build(t)
|
||||
|
||||
_, err := client.GetRRSet(t.Context(), "example.com", "groupA", "www", "TXT")
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestClient_Delete(t *testing.T) {
|
||||
client := setupTest(t, "DELETE /api/config/dns/namespaces/system/dns_zones/example.com/rrsets/groupA/www/TXT", http.StatusOK, "get.json")
|
||||
client := mockBuilder().
|
||||
Route("DELETE /api/config/dns/namespaces/system/dns_zones/example.com/rrsets/groupA/www/TXT",
|
||||
servermock.ResponseFromFixture("get.json")).
|
||||
Build(t)
|
||||
|
||||
result, err := client.DeleteRRSet(t.Context(), "example.com", "groupA", "www", "TXT")
|
||||
require.NoError(t, err)
|
||||
|
|
@ -162,14 +158,21 @@ func TestClient_Delete(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_Delete_error(t *testing.T) {
|
||||
client := setupTest(t, "DELETE /api/config/dns/namespaces/system/dns_zones/example.com/rrsets/groupA/www/TXT", http.StatusBadRequest, "")
|
||||
client := mockBuilder().
|
||||
Route("DELETE /api/config/dns/namespaces/system/dns_zones/example.com/rrsets/groupA/www/TXT",
|
||||
servermock.Noop().WithStatusCode(http.StatusBadRequest)).
|
||||
Build(t)
|
||||
|
||||
_, err := client.DeleteRRSet(t.Context(), "example.com", "groupA", "www", "TXT")
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestClient_Replace(t *testing.T) {
|
||||
client := setupTest(t, "PUT /api/config/dns/namespaces/system/dns_zones/example.com/rrsets/groupA/www/TXT", http.StatusOK, "get.json")
|
||||
client := mockBuilder().
|
||||
Route("PUT /api/config/dns/namespaces/system/dns_zones/example.com/rrsets/groupA/www/TXT",
|
||||
servermock.ResponseFromFixture("get.json"),
|
||||
servermock.CheckRequestJSONBody(`{"dns_zone_name":"example.com","group_name":"groupA","type":"TXT","rrset":{"description":"lego","ttl":60,"txt_record":{"name":"wwww","values":["txt"]}}}`)).
|
||||
Build(t)
|
||||
|
||||
rrSet := RRSet{
|
||||
Description: "lego",
|
||||
|
|
@ -202,7 +205,10 @@ func TestClient_Replace(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClient_Replace_error(t *testing.T) {
|
||||
client := setupTest(t, "PUT /api/config/dns/namespaces/system/dns_zones/example.com/rrsets/groupA/www/TXT", http.StatusBadRequest, "")
|
||||
client := mockBuilder().
|
||||
Route("PUT /api/config/dns/namespaces/system/dns_zones/example.com/rrsets/groupA/www/TXT",
|
||||
servermock.Noop().WithStatusCode(http.StatusBadRequest)).
|
||||
Build(t)
|
||||
|
||||
rrSet := RRSet{
|
||||
Description: "lego",
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/go-acme/lego/v4/platform/tester"
|
||||
"github.com/go-acme/lego/v4/platform/tester/servermock"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
|
@ -119,38 +120,41 @@ func TestDNSProvider(t *testing.T) {
|
|||
cleanupDeleteZoneRequestMock: cleanupDeleteZoneResponseMock,
|
||||
}
|
||||
|
||||
fakeKeyAuth := "XXXX"
|
||||
|
||||
regexpDate := regexp.MustCompile(`\[ACME Challenge [^\]:]*:[^\]]*\]`)
|
||||
|
||||
// start fake RPC server
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
require.Equal(t, "text/xml", r.Header.Get("Content-Type"), "invalid content type")
|
||||
provider := servermock.NewBuilder(
|
||||
func(server *httptest.Server) (*DNSProvider, error) {
|
||||
config := NewDefaultConfig()
|
||||
config.BaseURL = server.URL + "/"
|
||||
config.APIKey = "123412341234123412341234"
|
||||
|
||||
req, errS := io.ReadAll(r.Body)
|
||||
require.NoError(t, errS)
|
||||
return NewDNSProviderConfig(config)
|
||||
},
|
||||
servermock.CheckHeader().WithContentType("text/xml"),
|
||||
).
|
||||
Route("POST /", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
require.Equal(t, "text/xml", req.Header.Get("Content-Type"), "invalid content type")
|
||||
|
||||
req = regexpDate.ReplaceAllLiteral(req, []byte(`[ACME Challenge 01 Jan 16 00:00 +0000]`))
|
||||
resp, ok := serverResponses[string(req)]
|
||||
require.Truef(t, ok, "Server response for request not found: %s", string(req))
|
||||
body, errS := io.ReadAll(req.Body)
|
||||
require.NoError(t, errS)
|
||||
|
||||
_, errS = io.Copy(w, strings.NewReader(resp))
|
||||
require.NoError(t, errS)
|
||||
}))
|
||||
t.Cleanup(server.Close)
|
||||
body = regexpDate.ReplaceAllLiteral(body, []byte(`[ACME Challenge 01 Jan 16 00:00 +0000]`))
|
||||
resp, ok := serverResponses[string(body)]
|
||||
require.Truef(t, ok, "Server response for request not found: %s", string(body))
|
||||
|
||||
_, errS = io.Copy(rw, strings.NewReader(resp))
|
||||
require.NoError(t, errS)
|
||||
})).
|
||||
Route("/", servermock.DumpRequest()).
|
||||
Build(t)
|
||||
|
||||
fakeKeyAuth := "XXXX"
|
||||
|
||||
// define function to override findZoneByFqdn with
|
||||
fakeFindZoneByFqdn := func(fqdn string) (string, error) {
|
||||
return "example.com.", nil
|
||||
}
|
||||
|
||||
config := NewDefaultConfig()
|
||||
config.BaseURL = server.URL + "/"
|
||||
config.APIKey = "123412341234123412341234"
|
||||
|
||||
provider, err := NewDNSProviderConfig(config)
|
||||
require.NoError(t, err)
|
||||
|
||||
// override findZoneByFqdn function
|
||||
savedFindZoneByFqdn := provider.findZoneByFqdn
|
||||
t.Cleanup(func() {
|
||||
|
|
@ -159,7 +163,7 @@ func TestDNSProvider(t *testing.T) {
|
|||
provider.findZoneByFqdn = fakeFindZoneByFqdn
|
||||
|
||||
// run Present
|
||||
err = provider.Present("abc.def.example.com", "", fakeKeyAuth)
|
||||
err := provider.Present("abc.def.example.com", "", fakeKeyAuth)
|
||||
require.NoError(t, err)
|
||||
|
||||
// run CleanUp
|
||||
|
|
|
|||
99
providers/dns/gandi/internal/client_test.go
Normal file
99
providers/dns/gandi/internal/client_test.go
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
package internal
|
||||
|
||||
import (
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/go-acme/lego/v4/platform/tester/servermock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func mockBuilder() *servermock.Builder[*Client] {
|
||||
return servermock.NewBuilder[*Client](
|
||||
func(server *httptest.Server) (*Client, error) {
|
||||
client := NewClient("secret")
|
||||
client.BaseURL = server.URL
|
||||
client.HTTPClient = server.Client()
|
||||
|
||||
return client, nil
|
||||
},
|
||||
servermock.CheckHeader().WithContentType("text/xml"),
|
||||
)
|
||||
}
|
||||
|
||||
func TestClient_GetZoneID(t *testing.T) {
|
||||
client := mockBuilder().
|
||||
Route("POST /", servermock.ResponseFromFixture("get_zone_id.xml"),
|
||||
servermock.CheckRequestBodyFromFile("get_zone_id-request.xml").IgnoreWhitespace()).
|
||||
Build(t)
|
||||
|
||||
zoneID, err := client.GetZoneID(t.Context(), "example.com")
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, 1, zoneID)
|
||||
}
|
||||
|
||||
func TestClient_CloneZone(t *testing.T) {
|
||||
client := mockBuilder().
|
||||
Route("POST /", servermock.ResponseFromFixture("clone_zone.xml"),
|
||||
servermock.CheckRequestBodyFromFile("clone_zone-request.xml").IgnoreWhitespace()).
|
||||
Build(t)
|
||||
|
||||
zoneID, err := client.CloneZone(t.Context(), 6, "foo")
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, 1, zoneID)
|
||||
}
|
||||
|
||||
func TestClient_NewZoneVersion(t *testing.T) {
|
||||
client := mockBuilder().
|
||||
Route("POST /", servermock.ResponseFromFixture("new_zone_version.xml"),
|
||||
servermock.CheckRequestBodyFromFile("new_zone_version-request.xml").IgnoreWhitespace()).
|
||||
Build(t)
|
||||
|
||||
zoneID, err := client.NewZoneVersion(t.Context(), 6)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, 1, zoneID)
|
||||
}
|
||||
|
||||
func TestClient_AddTXTRecord(t *testing.T) {
|
||||
client := mockBuilder().
|
||||
Route("POST /", servermock.ResponseFromFixture("empty.xml"),
|
||||
servermock.CheckRequestBodyFromFile("add_txt_record-request.xml").IgnoreWhitespace()).
|
||||
Build(t)
|
||||
|
||||
err := client.AddTXTRecord(t.Context(), 1, 123, "foo", "content", 120)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestClient_SetZoneVersion(t *testing.T) {
|
||||
client := mockBuilder().
|
||||
Route("POST /", servermock.ResponseFromFixture("set_zone_version.xml"),
|
||||
servermock.CheckRequestBodyFromFile("set_zone_version-request.xml").IgnoreWhitespace()).
|
||||
Build(t)
|
||||
|
||||
err := client.SetZoneVersion(t.Context(), 1, 123)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestClient_SetZone(t *testing.T) {
|
||||
client := mockBuilder().
|
||||
Route("POST /", servermock.ResponseFromFixture("set_zone.xml"),
|
||||
servermock.CheckRequestBodyFromFile("set_zone-request.xml").IgnoreWhitespace()).
|
||||
Build(t)
|
||||
|
||||
err := client.SetZone(t.Context(), "example.com", 1)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestClient_DeleteZone(t *testing.T) {
|
||||
client := mockBuilder().
|
||||
Route("POST /", servermock.ResponseFromFixture("delete_zone.xml"),
|
||||
servermock.CheckRequestBodyFromFile("delete_zone-request.xml").IgnoreWhitespace()).
|
||||
Build(t)
|
||||
|
||||
err := client.DeleteZone(t.Context(), 1)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<methodCall>
|
||||
<methodName>domain.zone.record.add</methodName>
|
||||
<param>
|
||||
<value>
|
||||
<string>secret</string>
|
||||
</value>
|
||||
</param>
|
||||
<param>
|
||||
<value>
|
||||
<int>1</int>
|
||||
</value>
|
||||
</param>
|
||||
<param>
|
||||
<value>
|
||||
<int>123</int>
|
||||
</value>
|
||||
</param>
|
||||
<param>
|
||||
<value>
|
||||
<struct>
|
||||
<member>
|
||||
<name>type</name>
|
||||
<value>
|
||||
<string>TXT</string>
|
||||
</value>
|
||||
</member>
|
||||
<member>
|
||||
<name>name</name>
|
||||
<value>
|
||||
<string>foo</string>
|
||||
</value>
|
||||
</member>
|
||||
<member>
|
||||
<name>value</name>
|
||||
<value>
|
||||
<string>content</string>
|
||||
</value>
|
||||
</member>
|
||||
<member>
|
||||
<name>ttl</name>
|
||||
<value>
|
||||
<int>120</int>
|
||||
</value>
|
||||
</member>
|
||||
</struct>
|
||||
</value>
|
||||
</param>
|
||||
</methodCall>
|
||||
31
providers/dns/gandi/internal/fixtures/clone_zone-request.xml
Normal file
31
providers/dns/gandi/internal/fixtures/clone_zone-request.xml
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<methodCall>
|
||||
<methodName>domain.zone.clone</methodName>
|
||||
<param>
|
||||
<value>
|
||||
<string>secret</string>
|
||||
</value>
|
||||
</param>
|
||||
<param>
|
||||
<value>
|
||||
<int>6</int>
|
||||
</value>
|
||||
</param>
|
||||
<param>
|
||||
<value>
|
||||
<int>0</int>
|
||||
</value>
|
||||
</param>
|
||||
<param>
|
||||
<value>
|
||||
<struct>
|
||||
<member>
|
||||
<name>name</name>
|
||||
<value>
|
||||
<string>foo</string>
|
||||
</value>
|
||||
</member>
|
||||
</struct>
|
||||
</value>
|
||||
</param>
|
||||
</methodCall>
|
||||
22
providers/dns/gandi/internal/fixtures/clone_zone.xml
Normal file
22
providers/dns/gandi/internal/fixtures/clone_zone.xml
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
<responseStruct>
|
||||
<params>
|
||||
<param>
|
||||
<value>
|
||||
<struct>
|
||||
<member>
|
||||
<name>id</name>
|
||||
<value>
|
||||
<int>1</int>
|
||||
</value>
|
||||
</member>
|
||||
<member>
|
||||
<name>foo</name>
|
||||
<value>
|
||||
<int>2</int>
|
||||
</value>
|
||||
</member>
|
||||
</struct>
|
||||
</value>
|
||||
</param>
|
||||
</params>
|
||||
</responseStruct>
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<methodCall>
|
||||
<methodName>domain.zone.delete</methodName>
|
||||
<param>
|
||||
<value>
|
||||
<string>secret</string>
|
||||
</value>
|
||||
</param>
|
||||
<param>
|
||||
<value>
|
||||
<int>1</int>
|
||||
</value>
|
||||
</param>
|
||||
</methodCall>
|
||||
9
providers/dns/gandi/internal/fixtures/delete_zone.xml
Normal file
9
providers/dns/gandi/internal/fixtures/delete_zone.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<responseBool>
|
||||
<params>
|
||||
<param>
|
||||
<value>
|
||||
<boolean>true</boolean>
|
||||
</value>
|
||||
</param>
|
||||
</params>
|
||||
</responseBool>
|
||||
2
providers/dns/gandi/internal/fixtures/empty.xml
Normal file
2
providers/dns/gandi/internal/fixtures/empty.xml
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
<responseStruct>
|
||||
</responseStruct>
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue