mirror of
https://github.com/hashicorp/vault.git
synced 2025-08-08 23:57:01 +02:00
Move version out of SDK. For now it's a copy rather than move: the part not addressed by this change is sdk/helper/useragent.String, which we'll want to remove in favour of PluginString. That will have to wait until we've removed uses of useragent.String from all builtins.
715 lines
18 KiB
Go
715 lines
18 KiB
Go
package framework
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"io/ioutil"
|
|
"path/filepath"
|
|
"reflect"
|
|
"regexp"
|
|
"sort"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/go-test/deep"
|
|
"github.com/hashicorp/vault/sdk/helper/jsonutil"
|
|
"github.com/hashicorp/vault/sdk/helper/wrapping"
|
|
"github.com/hashicorp/vault/sdk/logical"
|
|
)
|
|
|
|
func TestOpenAPI_Regex(t *testing.T) {
|
|
t.Run("Required", func(t *testing.T) {
|
|
tests := []struct {
|
|
input string
|
|
captures []string
|
|
}{
|
|
{`/foo/bar/(?P<val>.*)`, []string{"val"}},
|
|
{`/foo/bar/` + GenericNameRegex("val"), []string{"val"}},
|
|
{`/foo/bar/` + GenericNameRegex("first") + "/b/" + GenericNameRegex("second"), []string{"first", "second"}},
|
|
{`/foo/bar`, []string{}},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
result := reqdRe.FindAllStringSubmatch(test.input, -1)
|
|
if len(result) != len(test.captures) {
|
|
t.Fatalf("Capture error (%s): expected %d matches, actual: %d", test.input, len(test.captures), len(result))
|
|
}
|
|
|
|
for i := 0; i < len(result); i++ {
|
|
if result[i][1] != test.captures[i] {
|
|
t.Fatalf("Capture error (%s): expected %s, actual: %s", test.input, test.captures[i], result[i][1])
|
|
}
|
|
}
|
|
}
|
|
})
|
|
t.Run("Optional", func(t *testing.T) {
|
|
input := "foo/(maybe/)?bar"
|
|
expStart := len("foo/")
|
|
expEnd := len(input) - len("bar")
|
|
|
|
match := optRe.FindStringIndex(input)
|
|
if diff := deep.Equal(match, []int{expStart, expEnd}); diff != nil {
|
|
t.Fatal(diff)
|
|
}
|
|
|
|
input = "/foo/maybe/bar"
|
|
match = optRe.FindStringIndex(input)
|
|
if match != nil {
|
|
t.Fatalf("Expected nil match (%s), got %+v", input, match)
|
|
}
|
|
})
|
|
t.Run("Alternation", func(t *testing.T) {
|
|
input := `(raw/?$|raw/(?P<path>.+))`
|
|
|
|
matches := altRe.FindAllStringSubmatch(input, -1)
|
|
exp1 := "raw/?$"
|
|
exp2 := "raw/(?P<path>.+)"
|
|
if matches[0][1] != exp1 || matches[0][2] != exp2 {
|
|
t.Fatalf("Capture error. Expected %s and %s, got %v", exp1, exp2, matches[0][1:])
|
|
}
|
|
|
|
input = `/foo/bar/` + GenericNameRegex("val")
|
|
|
|
matches = altRe.FindAllStringSubmatch(input, -1)
|
|
if matches != nil {
|
|
t.Fatalf("Expected nil match (%s), got %+v", input, matches)
|
|
}
|
|
})
|
|
t.Run("Alternation Fields", func(t *testing.T) {
|
|
input := `/foo/bar/(?P<type>auth|database|secret)/(?P<blah>a|b)`
|
|
|
|
act := altFieldsGroupRe.ReplaceAllStringFunc(input, func(s string) string {
|
|
return altFieldsRe.ReplaceAllString(s, ".+")
|
|
})
|
|
|
|
exp := "/foo/bar/(?P<type>.+)/(?P<blah>.+)"
|
|
if act != exp {
|
|
t.Fatalf("Replace error. Expected %s, got %v", exp, act)
|
|
}
|
|
})
|
|
t.Run("Path fields", func(t *testing.T) {
|
|
input := `/foo/bar/{inner}/baz/{outer}`
|
|
|
|
matches := pathFieldsRe.FindAllStringSubmatch(input, -1)
|
|
|
|
exp1 := "inner"
|
|
exp2 := "outer"
|
|
if matches[0][1] != exp1 || matches[1][1] != exp2 {
|
|
t.Fatalf("Capture error. Expected %s and %s, got %v", exp1, exp2, matches)
|
|
}
|
|
|
|
input = `/foo/bar/inner/baz/outer`
|
|
matches = pathFieldsRe.FindAllStringSubmatch(input, -1)
|
|
|
|
if matches != nil {
|
|
t.Fatalf("Expected nil match (%s), got %+v", input, matches)
|
|
}
|
|
})
|
|
t.Run("Filtering", func(t *testing.T) {
|
|
tests := []struct {
|
|
input string
|
|
regex *regexp.Regexp
|
|
output string
|
|
}{
|
|
{
|
|
input: `ab?cde^fg(hi?j$k`,
|
|
regex: cleanCharsRe,
|
|
output: "abcdefghijk",
|
|
},
|
|
{
|
|
input: `abcde/?`,
|
|
regex: cleanSuffixRe,
|
|
output: "abcde",
|
|
},
|
|
{
|
|
input: `abcde/?$`,
|
|
regex: cleanSuffixRe,
|
|
output: "abcde",
|
|
},
|
|
{
|
|
input: `abcde`,
|
|
regex: wsRe,
|
|
output: "abcde",
|
|
},
|
|
{
|
|
input: ` a b cd e `,
|
|
regex: wsRe,
|
|
output: "abcde",
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
result := test.regex.ReplaceAllString(test.input, "")
|
|
if result != test.output {
|
|
t.Fatalf("Clean Regex error (%s). Expected %s, got %s", test.input, test.output, result)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestOpenAPI_ExpandPattern(t *testing.T) {
|
|
tests := []struct {
|
|
inPattern string
|
|
outPathlets []string
|
|
}{
|
|
{"rekey/backup", []string{"rekey/backup"}},
|
|
{"rekey/backup$", []string{"rekey/backup"}},
|
|
{"auth/(?P<path>.+?)/tune$", []string{"auth/{path}/tune"}},
|
|
{"auth/(?P<path>.+?)/tune/(?P<more>.*?)$", []string{"auth/{path}/tune/{more}"}},
|
|
{"tools/hash(/(?P<urlalgorithm>.+))?", []string{
|
|
"tools/hash",
|
|
"tools/hash/{urlalgorithm}",
|
|
}},
|
|
{"(leases/)?renew(/(?P<url_lease_id>.+))?", []string{
|
|
"leases/renew",
|
|
"leases/renew/{url_lease_id}",
|
|
"renew",
|
|
"renew/{url_lease_id}",
|
|
}},
|
|
{`config/ui/headers/` + GenericNameRegex("header"), []string{"config/ui/headers/{header}"}},
|
|
{`leases/lookup/(?P<prefix>.+?)?`, []string{
|
|
"leases/lookup/",
|
|
"leases/lookup/{prefix}",
|
|
}},
|
|
{`(raw/?$|raw/(?P<path>.+))`, []string{
|
|
"raw",
|
|
"raw/{path}",
|
|
}},
|
|
{"lookup" + OptionalParamRegex("urltoken"), []string{
|
|
"lookup",
|
|
"lookup/{urltoken}",
|
|
}},
|
|
{"roles/?$", []string{
|
|
"roles",
|
|
}},
|
|
{"roles/?", []string{
|
|
"roles",
|
|
}},
|
|
{"accessors/$", []string{
|
|
"accessors/",
|
|
}},
|
|
{"verify/" + GenericNameRegex("name") + OptionalParamRegex("urlalgorithm"), []string{
|
|
"verify/{name}",
|
|
"verify/{name}/{urlalgorithm}",
|
|
}},
|
|
{"^plugins/catalog/(?P<type>auth|database|secret)/(?P<name>.+)$", []string{
|
|
"plugins/catalog/{type}/{name}",
|
|
}},
|
|
{"^plugins/catalog/(?P<type>auth|database|secret)/?$", []string{
|
|
"plugins/catalog/{type}",
|
|
}},
|
|
{"(pathOne|pathTwo)/", []string{"pathOne/", "pathTwo/"}},
|
|
{"(pathOne|pathTwo)/" + GenericNameRegex("name"), []string{"pathOne/{name}", "pathTwo/{name}"}},
|
|
{
|
|
"(pathOne|path-2|Path_3)/" + GenericNameRegex("name"),
|
|
[]string{"Path_3/{name}", "path-2/{name}", "pathOne/{name}"},
|
|
},
|
|
}
|
|
|
|
for i, test := range tests {
|
|
out := expandPattern(test.inPattern)
|
|
sort.Strings(out)
|
|
if !reflect.DeepEqual(out, test.outPathlets) {
|
|
t.Fatalf("Test %d: Expected %v got %v", i, test.outPathlets, out)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestOpenAPI_SplitFields(t *testing.T) {
|
|
fields := map[string]*FieldSchema{
|
|
"a": {Description: "path"},
|
|
"b": {Description: "body"},
|
|
"c": {Description: "body"},
|
|
"d": {Description: "body"},
|
|
"e": {Description: "path"},
|
|
}
|
|
|
|
pathFields, bodyFields := splitFields(fields, "some/{a}/path/{e}")
|
|
|
|
lp := len(pathFields)
|
|
lb := len(bodyFields)
|
|
l := len(fields)
|
|
if lp+lb != l {
|
|
t.Fatalf("split length error: %d + %d != %d", lp, lb, l)
|
|
}
|
|
|
|
for name, field := range pathFields {
|
|
if field.Description != "path" {
|
|
t.Fatalf("expected field %s to be in 'path', found in %s", name, field.Description)
|
|
}
|
|
}
|
|
for name, field := range bodyFields {
|
|
if field.Description != "body" {
|
|
t.Fatalf("expected field %s to be in 'body', found in %s", name, field.Description)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestOpenAPI_SpecialPaths(t *testing.T) {
|
|
tests := []struct {
|
|
pattern string
|
|
rootPaths []string
|
|
root bool
|
|
unauthPaths []string
|
|
unauth bool
|
|
}{
|
|
{"foo", []string{}, false, []string{"foo"}, true},
|
|
{"foo", []string{"foo"}, true, []string{"bar"}, false},
|
|
{"foo/bar", []string{"foo"}, false, []string{"foo/*"}, true},
|
|
{"foo/bar", []string{"foo/*"}, true, []string{"foo"}, false},
|
|
{"foo/", []string{"foo/*"}, true, []string{"a", "b", "foo/"}, true},
|
|
{"foo", []string{"foo*"}, true, []string{"a", "fo*"}, true},
|
|
{"foo/bar", []string{"a", "b", "foo/*"}, true, []string{"foo/baz/*"}, false},
|
|
}
|
|
for i, test := range tests {
|
|
doc := NewOASDocument("version")
|
|
path := Path{
|
|
Pattern: test.pattern,
|
|
}
|
|
sp := &logical.Paths{
|
|
Root: test.rootPaths,
|
|
Unauthenticated: test.unauthPaths,
|
|
}
|
|
err := documentPath(&path, sp, "kv", false, logical.TypeLogical, doc)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
result := test.root
|
|
if doc.Paths["/"+test.pattern].Sudo != result {
|
|
t.Fatalf("Test (root) %d: Expected %v got %v", i, test.root, result)
|
|
}
|
|
result = test.unauth
|
|
if doc.Paths["/"+test.pattern].Unauthenticated != result {
|
|
t.Fatalf("Test (unauth) %d: Expected %v got %v", i, test.unauth, result)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestOpenAPI_Paths(t *testing.T) {
|
|
origDepth := deep.MaxDepth
|
|
defer func() { deep.MaxDepth = origDepth }()
|
|
deep.MaxDepth = 20
|
|
|
|
t.Run("Legacy callbacks", func(t *testing.T) {
|
|
p := &Path{
|
|
Pattern: "lookup/" + GenericNameRegex("id"),
|
|
|
|
Fields: map[string]*FieldSchema{
|
|
"id": {
|
|
Type: TypeString,
|
|
Description: "My id parameter",
|
|
},
|
|
"token": {
|
|
Type: TypeString,
|
|
Description: "My token",
|
|
},
|
|
},
|
|
|
|
Callbacks: map[logical.Operation]OperationFunc{
|
|
logical.ReadOperation: nil,
|
|
logical.UpdateOperation: nil,
|
|
},
|
|
|
|
HelpSynopsis: "Synopsis",
|
|
HelpDescription: "Description",
|
|
}
|
|
|
|
sp := &logical.Paths{
|
|
Root: []string{},
|
|
Unauthenticated: []string{},
|
|
}
|
|
testPath(t, p, sp, expected("legacy"))
|
|
})
|
|
|
|
t.Run("Operations - All Operations", func(t *testing.T) {
|
|
p := &Path{
|
|
Pattern: "foo/" + GenericNameRegex("id"),
|
|
Fields: map[string]*FieldSchema{
|
|
"id": {
|
|
Type: TypeString,
|
|
Description: "id path parameter",
|
|
},
|
|
"flavors": {
|
|
Type: TypeCommaStringSlice,
|
|
Description: "the flavors",
|
|
},
|
|
"name": {
|
|
Type: TypeNameString,
|
|
Default: "Larry",
|
|
Description: "the name",
|
|
},
|
|
"age": {
|
|
Type: TypeInt,
|
|
Description: "the age",
|
|
AllowedValues: []interface{}{1, 2, 3},
|
|
Required: true,
|
|
DisplayAttrs: &DisplayAttributes{
|
|
Name: "Age",
|
|
Sensitive: true,
|
|
Group: "Some Group",
|
|
Value: 7,
|
|
},
|
|
},
|
|
"x-abc-token": {
|
|
Type: TypeHeader,
|
|
Description: "a header value",
|
|
AllowedValues: []interface{}{"a", "b", "c"},
|
|
},
|
|
"maximum": {
|
|
Type: TypeInt64,
|
|
Description: "a maximum value",
|
|
},
|
|
"format": {
|
|
Type: TypeString,
|
|
Description: "a query param",
|
|
Query: true,
|
|
},
|
|
},
|
|
HelpSynopsis: "Synopsis",
|
|
HelpDescription: "Description",
|
|
Operations: map[logical.Operation]OperationHandler{
|
|
logical.ReadOperation: &PathOperation{
|
|
Summary: "My Summary",
|
|
Description: "My Description",
|
|
},
|
|
logical.UpdateOperation: &PathOperation{
|
|
Summary: "Update Summary",
|
|
Description: "Update Description",
|
|
},
|
|
logical.CreateOperation: &PathOperation{
|
|
Summary: "Create Summary",
|
|
Description: "Create Description",
|
|
},
|
|
logical.ListOperation: &PathOperation{
|
|
Summary: "List Summary",
|
|
Description: "List Description",
|
|
},
|
|
logical.DeleteOperation: &PathOperation{
|
|
Summary: "This shouldn't show up",
|
|
Unpublished: true,
|
|
},
|
|
},
|
|
DisplayAttrs: &DisplayAttributes{
|
|
Navigation: true,
|
|
},
|
|
}
|
|
|
|
sp := &logical.Paths{
|
|
Root: []string{"foo*"},
|
|
}
|
|
testPath(t, p, sp, expected("operations"))
|
|
})
|
|
|
|
t.Run("Operations - List Only", func(t *testing.T) {
|
|
p := &Path{
|
|
Pattern: "foo/" + GenericNameRegex("id"),
|
|
Fields: map[string]*FieldSchema{
|
|
"id": {
|
|
Type: TypeString,
|
|
Description: "id path parameter",
|
|
},
|
|
"flavors": {
|
|
Type: TypeCommaStringSlice,
|
|
Description: "the flavors",
|
|
},
|
|
"name": {
|
|
Type: TypeNameString,
|
|
Default: "Larry",
|
|
Description: "the name",
|
|
},
|
|
"age": {
|
|
Type: TypeInt,
|
|
Description: "the age",
|
|
AllowedValues: []interface{}{1, 2, 3},
|
|
Required: true,
|
|
DisplayAttrs: &DisplayAttributes{
|
|
Name: "Age",
|
|
Sensitive: true,
|
|
Group: "Some Group",
|
|
Value: 7,
|
|
},
|
|
},
|
|
"x-abc-token": {
|
|
Type: TypeHeader,
|
|
Description: "a header value",
|
|
AllowedValues: []interface{}{"a", "b", "c"},
|
|
},
|
|
"format": {
|
|
Type: TypeString,
|
|
Description: "a query param",
|
|
Query: true,
|
|
},
|
|
},
|
|
HelpSynopsis: "Synopsis",
|
|
HelpDescription: "Description",
|
|
Operations: map[logical.Operation]OperationHandler{
|
|
logical.ListOperation: &PathOperation{
|
|
Summary: "List Summary",
|
|
Description: "List Description",
|
|
},
|
|
},
|
|
DisplayAttrs: &DisplayAttributes{
|
|
Navigation: true,
|
|
},
|
|
}
|
|
|
|
sp := &logical.Paths{
|
|
Root: []string{"foo*"},
|
|
}
|
|
testPath(t, p, sp, expected("operations_list"))
|
|
})
|
|
|
|
t.Run("Responses", func(t *testing.T) {
|
|
p := &Path{
|
|
Pattern: "foo",
|
|
HelpSynopsis: "Synopsis",
|
|
HelpDescription: "Description",
|
|
Operations: map[logical.Operation]OperationHandler{
|
|
logical.ReadOperation: &PathOperation{
|
|
Summary: "My Summary",
|
|
Description: "My Description",
|
|
Responses: map[int][]Response{
|
|
202: {{
|
|
Description: "Amazing",
|
|
Example: &logical.Response{
|
|
Data: map[string]interface{}{
|
|
"amount": 42,
|
|
},
|
|
},
|
|
Fields: map[string]*FieldSchema{
|
|
"field_a": {
|
|
Type: TypeString,
|
|
Description: "field_a description",
|
|
},
|
|
"field_b": {
|
|
Type: TypeBool,
|
|
Description: "field_b description",
|
|
},
|
|
},
|
|
}},
|
|
},
|
|
},
|
|
logical.DeleteOperation: &PathOperation{
|
|
Summary: "Delete stuff",
|
|
},
|
|
},
|
|
}
|
|
|
|
sp := &logical.Paths{
|
|
Unauthenticated: []string{"x", "y", "foo"},
|
|
}
|
|
|
|
testPath(t, p, sp, expected("responses"))
|
|
})
|
|
}
|
|
|
|
func TestOpenAPI_OperationID(t *testing.T) {
|
|
path1 := &Path{
|
|
Pattern: "foo/" + GenericNameRegex("id"),
|
|
Fields: map[string]*FieldSchema{
|
|
"id": {Type: TypeString},
|
|
},
|
|
Operations: map[logical.Operation]OperationHandler{
|
|
logical.ReadOperation: &PathOperation{},
|
|
logical.UpdateOperation: &PathOperation{},
|
|
logical.DeleteOperation: &PathOperation{},
|
|
},
|
|
}
|
|
|
|
path2 := &Path{
|
|
Pattern: "Foo/" + GenericNameRegex("id"),
|
|
Fields: map[string]*FieldSchema{
|
|
"id": {Type: TypeString},
|
|
},
|
|
Operations: map[logical.Operation]OperationHandler{
|
|
logical.ReadOperation: &PathOperation{},
|
|
},
|
|
}
|
|
|
|
for _, context := range []string{"", "bar"} {
|
|
doc := NewOASDocument("version")
|
|
err := documentPath(path1, nil, "kv", false, logical.TypeLogical, doc)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
err = documentPath(path2, nil, "kv", false, logical.TypeLogical, doc)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
doc.CreateOperationIDs(context)
|
|
|
|
tests := []struct {
|
|
path string
|
|
op string
|
|
opID string
|
|
}{
|
|
{"/Foo/{id}", "get", "getFooId"},
|
|
{"/foo/{id}", "get", "getFooId_2"},
|
|
{"/foo/{id}", "post", "postFooId"},
|
|
{"/foo/{id}", "delete", "deleteFooId"},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
actual := getPathOp(doc.Paths[test.path], test.op).OperationID
|
|
expected := test.opID
|
|
if context != "" {
|
|
expected += "_" + context
|
|
}
|
|
|
|
if actual != expected {
|
|
t.Fatalf("expected %v, got %v", expected, actual)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestOpenAPI_CustomDecoder(t *testing.T) {
|
|
p := &Path{
|
|
Pattern: "foo",
|
|
HelpSynopsis: "Synopsis",
|
|
Operations: map[logical.Operation]OperationHandler{
|
|
logical.ReadOperation: &PathOperation{
|
|
Summary: "My Summary",
|
|
Responses: map[int][]Response{
|
|
100: {{
|
|
Description: "OK",
|
|
Example: &logical.Response{
|
|
Data: map[string]interface{}{
|
|
"foo": 42,
|
|
},
|
|
},
|
|
}},
|
|
200: {{
|
|
Description: "Good",
|
|
Example: (*logical.Response)(nil),
|
|
}},
|
|
599: {{
|
|
Description: "Bad",
|
|
}},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
docOrig := NewOASDocument("version")
|
|
err := documentPath(p, nil, "kv", false, logical.TypeLogical, docOrig)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
docJSON := mustJSONMarshal(t, docOrig)
|
|
|
|
var intermediate map[string]interface{}
|
|
if err := jsonutil.DecodeJSON(docJSON, &intermediate); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
docNew, err := NewOASDocumentFromMap(intermediate)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
docNewJSON := mustJSONMarshal(t, docNew)
|
|
|
|
if diff := deep.Equal(docJSON, docNewJSON); diff != nil {
|
|
t.Fatal(diff)
|
|
}
|
|
}
|
|
|
|
func TestOpenAPI_CleanResponse(t *testing.T) {
|
|
// Verify that an all-null input results in empty JSON
|
|
orig := &logical.Response{}
|
|
|
|
cr := cleanResponse(orig)
|
|
|
|
newJSON := mustJSONMarshal(t, cr)
|
|
|
|
if !bytes.Equal(newJSON, []byte("{}")) {
|
|
t.Fatalf("expected {}, got: %q", newJSON)
|
|
}
|
|
|
|
// Verify that all non-null inputs results in JSON that matches the marshalling of
|
|
// logical.Response. This will fail if logical.Response changes without a corresponding
|
|
// change to cleanResponse()
|
|
orig = &logical.Response{
|
|
Secret: new(logical.Secret),
|
|
Auth: new(logical.Auth),
|
|
Data: map[string]interface{}{"foo": 42},
|
|
Redirect: "foo",
|
|
Warnings: []string{"foo"},
|
|
WrapInfo: &wrapping.ResponseWrapInfo{Token: "foo"},
|
|
Headers: map[string][]string{"foo": {"bar"}},
|
|
}
|
|
origJSON := mustJSONMarshal(t, orig)
|
|
|
|
cr = cleanResponse(orig)
|
|
|
|
cleanJSON := mustJSONMarshal(t, cr)
|
|
|
|
if diff := deep.Equal(origJSON, cleanJSON); diff != nil {
|
|
t.Fatal(diff)
|
|
}
|
|
}
|
|
|
|
func testPath(t *testing.T, path *Path, sp *logical.Paths, expectedJSON string) {
|
|
t.Helper()
|
|
|
|
doc := NewOASDocument("dummyversion")
|
|
if err := documentPath(path, sp, "kv", false, logical.TypeLogical, doc); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
doc.CreateOperationIDs("")
|
|
|
|
docJSON, err := json.MarshalIndent(doc, "", " ")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Compare json by first decoding, then comparing with a deep equality check.
|
|
var expected, actual interface{}
|
|
if err := jsonutil.DecodeJSON(docJSON, &actual); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if err := jsonutil.DecodeJSON([]byte(expectedJSON), &expected); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if diff := deep.Equal(actual, expected); diff != nil {
|
|
// fmt.Println(string(docJSON)) // uncomment to debug generated JSON (very helpful when fixing tests)
|
|
t.Fatal(diff)
|
|
}
|
|
}
|
|
|
|
func getPathOp(pi *OASPathItem, op string) *OASOperation {
|
|
switch op {
|
|
case "get":
|
|
return pi.Get
|
|
case "post":
|
|
return pi.Post
|
|
case "delete":
|
|
return pi.Delete
|
|
default:
|
|
panic("unexpected operation: " + op)
|
|
}
|
|
}
|
|
|
|
func expected(name string) string {
|
|
data, err := ioutil.ReadFile(filepath.Join("testdata", name+".json"))
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
content := strings.Replace(string(data), "<vault_version>", "dummyversion", 1)
|
|
|
|
return content
|
|
}
|
|
|
|
func mustJSONMarshal(t *testing.T, data interface{}) []byte {
|
|
j, err := json.MarshalIndent(data, "", " ")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return j
|
|
}
|