86 lines
4.0 KiB
Go
86 lines
4.0 KiB
Go
package contract
|
|
|
|
import (
|
|
"encoding/json"
|
|
"reflect"
|
|
"slices"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func limit(n int64) *int64 { return &n }
|
|
|
|
// A route is read strictly and whole (issue 774): what the contract names is admitted, what it does not, or a route
|
|
// it cannot serve, is refused naming why; and the published schema names exactly the fields the decoder reads.
|
|
func TestARouteIsReadStrictly(t *testing.T) {
|
|
for _, c := range []struct {
|
|
raw, refused string
|
|
want Request
|
|
}{
|
|
{raw: `{"name":"app.example.org","internal-name":"app.anchor.internal","label":"app","endpoint":"web","port":8080}`,
|
|
want: Request{Name: "app.example.org", InternalName: "app.anchor.internal", Label: "app", Endpoint: "web", Port: 8080}},
|
|
{raw: `{"internal-name":"app.anchor.internal","port":443,"scheme":"https","insecure":true,"max-request-body":1048576}`,
|
|
want: Request{InternalName: "app.anchor.internal", Port: 443, Scheme: "https", Insecure: true, MaxRequestBody: limit(1048576)}},
|
|
{raw: `{"name":"git.example.org","path":"/api/internal","deny":true,"priority":100000}`,
|
|
want: Request{Name: "git.example.org", Path: "/api/internal", Deny: true, Priority: 100000}},
|
|
{raw: `{"name":"old.example.org","redirect":"https://new.example.org"}`,
|
|
want: Request{Name: "old.example.org", Redirect: "https://new.example.org"}},
|
|
{raw: `{"name":"admin.example.org","port":80,"auth":"/run/secrets/admin"}`,
|
|
want: Request{Name: "admin.example.org", Port: 80, Auth: "/run/secrets/admin"}},
|
|
{raw: `{"label":"app","port":8080}`, refused: "names nothing"},
|
|
{raw: `{"name":"app.example.org"}`, refused: "gives no port"},
|
|
{raw: `{"name":"app.example.org","port":70000}`, refused: "not a port"},
|
|
{raw: `{"name":"app.example.org","port":80.5}`, refused: "not a route"},
|
|
{raw: `{"name":"app.example.org","port":"80"}`, refused: "not a route"},
|
|
{raw: `{"name":"app.example.org","port":80,"scheme":"ftp"}`, refused: "neither http nor https"},
|
|
{raw: `{"name":"app.example.org","port":80,"max-request-body":-1}`, refused: "max-request-body"},
|
|
{raw: `{"name":"app.example.org","port":80,"max-request-body":0}`, refused: "max-request-body"},
|
|
{raw: `{"name":"app.example.org","port":80,"auth":"admin:$2y$05$abc"}`, refused: "credential"},
|
|
{raw: `{"name":"app.example.org","port":80,"host":"x"}`, refused: "unknown field \"host\""},
|
|
{raw: `{"name":"app.example.org","port":80} {}`, refused: "more than one"},
|
|
} {
|
|
got, err := DecodeRequest([]byte(c.raw))
|
|
if c.refused != "" {
|
|
if err == nil || !strings.Contains(err.Error(), c.refused) {
|
|
t.Errorf("DecodeRequest(%s) = %+v, %v; want refused for %q", c.raw, got, err, c.refused)
|
|
}
|
|
continue
|
|
}
|
|
if err != nil || !reflect.DeepEqual(got, c.want) {
|
|
t.Errorf("DecodeRequest(%s) = %+v, %v; want %+v", c.raw, got, err, c.want)
|
|
continue
|
|
}
|
|
// What it encodes, it reads back the same.
|
|
if raw, err := got.Encode(); err != nil {
|
|
t.Errorf("Encode(%+v): %v", got, err)
|
|
} else if again, err := DecodeRequest(raw); err != nil || !reflect.DeepEqual(again, got) {
|
|
t.Errorf("Encode then DecodeRequest of %+v = %+v, %v", got, again, err)
|
|
}
|
|
}
|
|
if (Request{Path: "api"}).Prefix() != "/api" || (Request{}).Prefix() != "" || (Request{Scheme: " HTTPS"}).SchemeOrDefault() != "https" ||
|
|
(Request{}).SchemeOrDefault() != "http" || (Request{Redirect: "x"}).Forwards() {
|
|
t.Error("Prefix, SchemeOrDefault or Forwards says otherwise than the contract")
|
|
}
|
|
|
|
var schema struct {
|
|
Properties map[string]json.RawMessage `json:"properties"`
|
|
AdditionalProperties bool `json:"additionalProperties"`
|
|
}
|
|
if err := json.Unmarshal(Schema, &schema); err != nil || schema.AdditionalProperties {
|
|
t.Fatalf("the schema is not a closed object: %v", err)
|
|
}
|
|
var tags []string
|
|
for f := range reflect.TypeFor[Request]().Fields() {
|
|
tags = append(tags, strings.Split(f.Tag.Get("json"), ",")[0])
|
|
}
|
|
var said []string
|
|
for name := range schema.Properties {
|
|
said = append(said, name)
|
|
}
|
|
slices.Sort(tags)
|
|
slices.Sort(said)
|
|
if !slices.Equal(tags, said) {
|
|
t.Fatalf("the schema names %v, the decoder reads %v", said, tags)
|
|
}
|
|
}
|