// Copyright 2016 Google LLC // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package disco import ( "io/ioutil" "reflect" "strings" "testing" ) var stringSchema = &Schema{ Type: "string", Kind: SimpleKind, } func TestDocument(t *testing.T) { bytes, err := ioutil.ReadFile("testdata/test-api.json") if err != nil { t.Fatal(err) } got, err := NewDocument(bytes) if err != nil { t.Fatal(err) } want := &Document{ ID: "storage:v1", Name: "storage", Version: "v1", Title: "Cloud Storage JSON API", RootURL: "https://www.googleapis.com/", ServicePath: "storage/v1/", BasePath: "/storage/v1/", DocumentationLink: "https://developers.google.com/storage/docs/json_api/", Auth: Auth{ OAuth2Scopes: []Scope{ {"https://www.googleapis.com/auth/cloud-platform", "View and manage your data across Google Cloud Platform services"}, {"https://www.googleapis.com/auth/cloud-platform.read-only", "View your data across Google Cloud Platform services"}, {"https://www.googleapis.com/auth/devstorage.full_control", "Manage your data and permissions in Google Cloud Storage"}, {"https://www.googleapis.com/auth/devstorage.read_only", "View your data in Google Cloud Storage"}, {"https://www.googleapis.com/auth/devstorage.read_write", "Manage your data in Google Cloud Storage"}, }, }, Features: []string{"dataWrapper"}, Schemas: map[string]*Schema{ "Bucket": { Name: "Bucket", ID: "Bucket", Type: "object", Description: "A bucket.", Kind: StructKind, Properties: []*Property{ {"cors", &Schema{ Type: "array", Kind: ArrayKind, ItemSchema: &Schema{ Type: "object", Kind: StructKind, Properties: []*Property{ {"maxAgeSeconds", &Schema{ Type: "integer", Format: "int32", Kind: SimpleKind, }}, {"method", &Schema{ Type: "array", Kind: ArrayKind, ItemSchema: stringSchema, }}, }, }, }}, {"id", stringSchema}, {"kind", &Schema{ Type: "string", Kind: SimpleKind, Default: "storage#bucket", }}, }, }, "Buckets": { ID: "Buckets", Name: "Buckets", Type: "object", Kind: StructKind, Properties: []*Property{ {"items", &Schema{ Type: "array", Kind: ArrayKind, ItemSchema: &Schema{ Kind: ReferenceKind, Ref: "Bucket", RefSchema: nil, }, }}, }, }, "VariantExample": { ID: "VariantExample", Name: "VariantExample", Type: "object", Kind: StructKind, Variant: &Variant{ Discriminant: "type", Map: []*VariantMapItem{ {TypeValue: "Bucket", Ref: "Bucket"}, {TypeValue: "Buckets", Ref: "Buckets"}, }, }, }, }, Methods: MethodList{ &Method{ Name: "getCertForOpenIdConnect", ID: "oauth2.getCertForOpenIdConnect", Path: "oauth2/v1/certs", HTTPMethod: "GET", Response: &Schema{Ref: "Bucket", Kind: ReferenceKind}, }, }, Resources: ResourceList{ &Resource{ Name: "buckets", FullName: ".buckets", Methods: MethodList{ &Method{ Name: "get", ID: "storage.buckets.get", Path: "b/{bucket}", HTTPMethod: "GET", Description: "d", Parameters: ParameterList{ &Parameter{ Name: "bucket", Schema: Schema{ Type: "string", }, Required: true, Location: "path", }, &Parameter{ Name: "ifMetagenerationMatch", Schema: Schema{ Type: "string", Format: "int64", }, Location: "query", }, &Parameter{ Name: "projection", Schema: Schema{ Type: "string", Enums: []string{"full", "noAcl"}, EnumDescriptions: []string{ "Include all properties.", "Omit owner, acl and defaultObjectAcl properties.", }, }, Location: "query", }, }, ParameterOrder: []string{"bucket"}, Response: &Schema{Ref: "Bucket", Kind: ReferenceKind}, Scopes: []string{ "https://www.googleapis.com/auth/cloud-platform", "https://www.googleapis.com/auth/cloud-platform.read-only", "https://www.googleapis.com/auth/devstorage.full_control", "https://www.googleapis.com/auth/devstorage.read_only", "https://www.googleapis.com/auth/devstorage.read_write", }, SupportsMediaDownload: true, MediaUpload: &MediaUpload{ Accept: []string{"application/octet-stream"}, MaxSize: "1GB", Protocols: map[string]Protocol{ "simple": { Multipart: true, Path: "/upload/customDataSources/{customDataSourceId}/uploads", }, "resumable": { Multipart: true, Path: "/resumable/upload/customDataSources/{customDataSourceId}/uploads", }, }, }, }, }, }, }, } // Resolve schema references. bucket := want.Schemas["Bucket"] want.Schemas["Buckets"].Properties[0].Schema.ItemSchema.RefSchema = bucket want.Methods[0].Response.RefSchema = bucket want.Resources[0].Methods[0].Response.RefSchema = bucket for k, gs := range got.Schemas { ws := want.Schemas[k] if !reflect.DeepEqual(gs, ws) { t.Fatalf("schema %s: got\n%+v\nwant\n%+v", k, gs, ws) } } if len(got.Schemas) != len(want.Schemas) { t.Errorf("want %d schemas, got %d", len(got.Schemas), len(want.Schemas)) } compareMethodLists(t, got.Methods, want.Methods) for i, gr := range got.Resources { wr := want.Resources[i] compareMethodLists(t, gr.Methods, wr.Methods) if !reflect.DeepEqual(gr, wr) { t.Fatalf("resource %d: got\n%+v\nwant\n%+v", i, gr, wr) } } if len(got.Resources) != len(want.Resources) { t.Errorf("want %d resources, got %d", len(got.Resources), len(want.Resources)) } if !reflect.DeepEqual(got, want) { t.Errorf("got\n%+v\nwant\n%+v", got, want) } } func compareMethodLists(t *testing.T, got, want MethodList) { if len(got) != len(want) { t.Fatalf("got %d methods, want %d", len(got), len(want)) } for i, gm := range got { gm.JSONMap = nil // don't compare the raw JSON wm := want[i] if !reflect.DeepEqual(gm, wm) { t.Errorf("#%d: got\n%+v\nwant\n%+v", i, gm, wm) } } } func TestDocumentErrors(t *testing.T) { for _, in := range []string{ `{"name": "X"`, // malformed JSON `{"id": 3}`, // ID is an int instead of a string `{"auth": "oauth2": { "scopes": "string" }}`, // wrong auth structure } { _, err := NewDocument([]byte(in)) if err == nil { t.Errorf("%s: got nil, want error", in) } } } func TestSchemaErrors(t *testing.T) { for _, s := range []*Schema{ {Type: "array"}, // missing item schema {Type: "string", ItemSchema: &Schema{}}, // items w/o array {Type: "moose"}, // bad kind {Ref: "Thing"}, // unresolved reference } { if err := s.init(nil); err == nil { t.Errorf("%+v: got nil, want error", s) } } } func TestErrorDoc(t *testing.T) { bytes, err := ioutil.ReadFile("testdata/error.json") if err != nil { t.Fatal(err) } if _, err := NewDocument(bytes); err == nil { t.Error("got nil, want error") } else if !strings.Contains(err.Error(), "404") { t.Errorf("got %v, want 404", err) } }