Skip to content

Commit

Permalink
chore: add unit tests for spec parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
odsod committed Mar 31, 2023
1 parent 29c2e21 commit 681e3a0
Show file tree
Hide file tree
Showing 17 changed files with 293 additions and 27 deletions.
4 changes: 1 addition & 3 deletions catalog/client_entities_getbyid.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,7 @@ func (c *Client) GetEntityByUID(ctx context.Context, request *GetEntityByUIDRequ
}); err != nil {
return nil, err
}
entity := Entity{
Raw: rawEntity,
}
var entity Entity
if err := json.Unmarshal(rawEntity, &entity); err != nil {
return nil, err
}
Expand Down
4 changes: 1 addition & 3 deletions catalog/client_entities_getbyname.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,7 @@ func (c *Client) GetEntityByName(ctx context.Context, request *GetEntityByNameRe
}); err != nil {
return nil, err
}
entity := Entity{
Raw: rawEntity,
}
var entity Entity
if err := json.Unmarshal(rawEntity, &entity); err != nil {
return nil, err
}
Expand Down
20 changes: 5 additions & 15 deletions catalog/client_entities_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ func (c *Client) ListEntities(ctx context.Context, request *ListEntitiesRequest)
if request.After != "" {
query.Set("after", request.After)
}
var rawEntities []json.RawMessage
var entities []*Entity
var nextPageToken string
if err := c.get(ctx, path, query, func(response *http.Response) error {
for _, link := range response.Header.Values("link") {
Expand All @@ -67,22 +67,12 @@ func (c *Client) ListEntities(ctx context.Context, request *ListEntitiesRequest)
nextPageToken = linkURL.Query().Get("after")
}
}
return json.NewDecoder(response.Body).Decode(&rawEntities)
return json.NewDecoder(response.Body).Decode(&entities)
}); err != nil {
return nil, err
}
response := ListEntitiesResponse{
Entities: make([]*Entity, 0, len(rawEntities)),
return &ListEntitiesResponse{
Entities: entities,
NextPageToken: nextPageToken,
}
for _, rawEntity := range rawEntities {
entity := &Entity{
Raw: rawEntity,
}
if err := json.Unmarshal(rawEntity, &entity); err != nil {
return nil, err
}
response.Entities = append(response.Entities, entity)
}
return &response, nil
}, nil
}
29 changes: 24 additions & 5 deletions catalog/entity.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,38 @@ import (
// An Entity in the software catalog.
type Entity struct {
// APIVersion is the version of specification format for this particular entity.
APIVersion string `json:"apiVersion"`
APIVersion string

// Kind is the high-level entity type.
Kind EntityKind `json:"kind"`
Kind EntityKind

// Metadata related to the entity.
Metadata EntityMetadata `json:"metadata"`
Metadata EntityMetadata

// Relations that this entity has with other entities.
Relations []EntityRelation `json:"relations,omitempty"`
Relations []EntityRelation

// Raw entity JSON message.
Raw json.RawMessage `json:"-"`
Raw json.RawMessage
}

// UnmarshalJSON implements [json.Unmarshaler].
func (e *Entity) UnmarshalJSON(data []byte) error {
var fields struct {
APIVersion string `json:"apiVersion"`
Kind EntityKind `json:"kind"`
Metadata EntityMetadata `json:"metadata"`
Relations []EntityRelation `json:"relations,omitempty"`
}
if err := json.Unmarshal(data, &fields); err != nil {
return err
}
e.APIVersion = fields.APIVersion
e.Kind = fields.Kind
e.Metadata = fields.Metadata
e.Relations = fields.Relations
e.Raw = data
return nil
}

// APISpec decodes the entity's spec as a [APISpec].
Expand Down
26 changes: 26 additions & 0 deletions catalog/spec_api_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package catalog

import (
"encoding/json"
"testing"

"gotest.tools/v3/assert"
)

func TestEntity_APISpec(t *testing.T) {
//nolint: lll
const component = `{"apiVersion":"backstage.io/v1alpha1","kind":"API","metadata":{"name":"artist-api","description":"Retrieve artist details"},"spec":{"type":"openapi","lifecycle":"production","owner":"artist-relations-team","system":"artist-engagement-portal","definition":"openapi: \"3.0.0\"\ninfo:\n version: 1.0.0\n title: Artist API\n license:\n name: MIT\nservers:\n - url: http://artist.spotify.net/v1\npaths:\n /artists:\n get:\n summary: List all artists\n...\n"}}`
var entity Entity
assert.NilError(t, json.Unmarshal([]byte(component), &entity))
expected := &APISpec{
Type: "openapi",
Lifecycle: "production",
Owner: "artist-relations-team",
System: "artist-engagement-portal",
//nolint: lll
Definition: "openapi: \"3.0.0\"\ninfo:\n version: 1.0.0\n title: Artist API\n license:\n name: MIT\nservers:\n - url: http://artist.spotify.net/v1\npaths:\n /artists:\n get:\n summary: List all artists\n...\n",
}
actual, err := entity.APISpec()
assert.NilError(t, err)
assert.DeepEqual(t, expected, actual)
}
24 changes: 24 additions & 0 deletions catalog/spec_component_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package catalog

import (
"encoding/json"
"testing"

"gotest.tools/v3/assert"
)

func TestEntity_ComponentSpec(t *testing.T) {
//nolint: lll
const component = "{\"apiVersion\":\"backstage.io/v1alpha1\",\"kind\":\"Component\",\"metadata\":{\"name\":\"petstore\",\"description\":\"[The Petstore](http://petstore.example.com) is an example API used to show features of the OpenAPI spec.\\n- First item\\n- Second item\\n\",\"links\":[{\"url\":\"https://github.com/swagger-api/swagger-petstore\",\"title\":\"GitHub Repo\",\"icon\":\"github\"}]},\"spec\":{\"type\":\"service\",\"lifecycle\":\"experimental\",\"owner\":\"team-c\",\"providesApis\":[\"petstore\",\"streetlights\",\"hello-world\"]}}"
var entity Entity
assert.NilError(t, json.Unmarshal([]byte(component), &entity))
expected := &ComponentSpec{
Type: "service",
Lifecycle: "experimental",
Owner: "team-c",
ProvidesAPIs: []string{"petstore", "streetlights", "hello-world"},
}
actual, err := entity.ComponentSpec()
assert.NilError(t, err)
assert.DeepEqual(t, expected, actual)
}
21 changes: 21 additions & 0 deletions catalog/spec_domain_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package catalog

import (
"encoding/json"
"testing"

"gotest.tools/v3/assert"
)

func TestEntity_DomainSpec(t *testing.T) {
//nolint: lll
const domain = `{"apiVersion":"backstage.io/v1alpha1","kind":"Domain","metadata":{"name":"playback","description":"Everything related to audio playback"},"spec":{"owner":"user:frank.tiernan"}}`
var entity Entity
assert.NilError(t, json.Unmarshal([]byte(domain), &entity))
expected := &DomainSpec{
Owner: "user:frank.tiernan",
}
actual, err := entity.DomainSpec()
assert.NilError(t, err)
assert.DeepEqual(t, expected, actual)
}
29 changes: 29 additions & 0 deletions catalog/spec_group_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package catalog

import (
"encoding/json"
"testing"

"gotest.tools/v3/assert"
)

func TestEntity_GroupSpec(t *testing.T) {
//nolint: lll
const domain = `{"apiVersion":"backstage.io/v1alpha1","kind":"Group","metadata":{"name":"infrastructure","description":"The infra business unit"},"spec":{"type":"business-unit","profile":{"displayName":"Infrastructure","email":"infrastructure@example.com","picture":"https://example.com/groups/bu-infrastructure.jpeg"},"parent":"ops","children":["backstage","other"],"members":["jdoe"]}}`
var entity Entity
assert.NilError(t, json.Unmarshal([]byte(domain), &entity))
expected := &GroupSpec{
Type: "business-unit",
Profile: Profile{
DisplayName: "Infrastructure",
Email: "infrastructure@example.com",
Picture: "https://example.com/groups/bu-infrastructure.jpeg",
},
Parent: "ops",
Children: []string{"backstage", "other"},
Members: []string{"jdoe"},
}
actual, err := entity.GroupSpec()
assert.NilError(t, err)
assert.DeepEqual(t, expected, actual)
}
25 changes: 25 additions & 0 deletions catalog/spec_location_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package catalog

import (
"encoding/json"
"testing"

"gotest.tools/v3/assert"
)

func TestEntity_LocationSpec(t *testing.T) {
//nolint: lll
const domain = `{"apiVersion":"backstage.io/v1alpha1","kind":"Location","metadata":{"name":"org-data"},"spec":{"type":"url","targets":["https://github.com/myorg/myproject/org-data-dump/catalog-info-staff.yaml","https://github.com/myorg/myproject/org-data-dump/catalog-info-consultants.yaml"]}}`
var entity Entity
assert.NilError(t, json.Unmarshal([]byte(domain), &entity))
expected := &LocationSpec{
Type: "url",
Targets: []string{
"https://github.com/myorg/myproject/org-data-dump/catalog-info-staff.yaml",
"https://github.com/myorg/myproject/org-data-dump/catalog-info-consultants.yaml",
},
}
actual, err := entity.LocationSpec()
assert.NilError(t, err)
assert.DeepEqual(t, expected, actual)
}
23 changes: 23 additions & 0 deletions catalog/spec_resource_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package catalog

import (
"encoding/json"
"testing"

"gotest.tools/v3/assert"
)

func TestEntity_ResourceSpec(t *testing.T) {
//nolint: lll
const domain = `{"apiVersion":"backstage.io/v1alpha1","kind":"Resource","metadata":{"name":"artists-db","description":"Stores artist details"},"spec":{"type":"database","owner":"team-a","system":"artist-engagement-portal"}}`
var entity Entity
assert.NilError(t, json.Unmarshal([]byte(domain), &entity))
expected := &ResourceSpec{
Type: "database",
Owner: "team-a",
System: "artist-engagement-portal",
}
actual, err := entity.ResourceSpec()
assert.NilError(t, err)
assert.DeepEqual(t, expected, actual)
}
2 changes: 1 addition & 1 deletion catalog/spec_system.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ type SystemSpec struct {
// An entity reference to the owner of the system. This field is required.
Owner string `json:"owner"`
// An entity reference to the domain that the system belongs to.
Domain string `json:"system,omitempty"`
Domain string `json:"domain,omitempty"`
}
22 changes: 22 additions & 0 deletions catalog/spec_system_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package catalog

import (
"encoding/json"
"testing"

"gotest.tools/v3/assert"
)

func TestEntity_SystemSpec(t *testing.T) {
//nolint: lll
const domain = `{"apiVersion":"backstage.io/v1alpha1","kind":"System","metadata":{"name":"podcast","description":"Podcast playback"},"spec":{"owner":"team-b","domain":"playback"}}`
var entity Entity
assert.NilError(t, json.Unmarshal([]byte(domain), &entity))
expected := &SystemSpec{
Owner: "team-b",
Domain: "playback",
}
actual, err := entity.SystemSpec()
assert.NilError(t, err)
assert.DeepEqual(t, expected, actual)
}
31 changes: 31 additions & 0 deletions catalog/spec_template_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package catalog

import (
"encoding/json"
"testing"

"gotest.tools/v3/assert"
)

func TestEntity_TemplateSpec(t *testing.T) {
//nolint: lll
const domain = `{"apiVersion":"backstage.io/v1beta2","kind":"Template","metadata":{"name":"v1beta2-demo","title":"Test Action template","description":"scaffolder v1beta2 template demo"},"spec":{"owner":"backstage/techdocs-core","type":"service","parameters":[{"title":"Fill in some steps","required":["name"],"properties":{"name":{"title":"Name","type":"string","description":"Unique name of the component","ui:autofocus":true,"ui:options":{"rows":5}}}},{"title":"Choose a location","required":["repoUrl"],"properties":{"repoUrl":{"title":"Repository Location","type":"string","ui:field":"RepoUrlPicker","ui:options":{"allowedHosts":["github.com"]}}}}],"steps":[{"id":"fetch-base","name":"Fetch Base","action":"fetch:template","input":{"url":"./template","values":{"name":"{{ parameters.name }}"}}},{"id":"fetch-docs","name":"Fetch Docs","action":"fetch:plain","input":{"targetPath":"./community","url":"https://github.com/backstage/community/tree/main/backstage-community-sessions"}}]}}`
var entity Entity
assert.NilError(t, json.Unmarshal([]byte(domain), &entity))
//nolint: lll
expected := &TemplateSpec{
Type: "service",
Owner: "backstage/techdocs-core",
RawParameters: []json.RawMessage{
json.RawMessage(`{"title":"Fill in some steps","required":["name"],"properties":{"name":{"title":"Name","type":"string","description":"Unique name of the component","ui:autofocus":true,"ui:options":{"rows":5}}}}`),
json.RawMessage(`{"title":"Choose a location","required":["repoUrl"],"properties":{"repoUrl":{"title":"Repository Location","type":"string","ui:field":"RepoUrlPicker","ui:options":{"allowedHosts":["github.com"]}}}}`),
},
RawSteps: []json.RawMessage{
json.RawMessage(`{"id":"fetch-base","name":"Fetch Base","action":"fetch:template","input":{"url":"./template","values":{"name":"{{ parameters.name }}"}}}`),
json.RawMessage(`{"id":"fetch-docs","name":"Fetch Docs","action":"fetch:plain","input":{"targetPath":"./community","url":"https://github.com/backstage/community/tree/main/backstage-community-sessions"}}`),
},
}
actual, err := entity.TemplateSpec()
assert.NilError(t, err)
assert.DeepEqual(t, expected, actual)
}
26 changes: 26 additions & 0 deletions catalog/spec_user_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package catalog

import (
"encoding/json"
"testing"

"gotest.tools/v3/assert"
)

func TestEntity_UserSpec(t *testing.T) {
//nolint: lll
const component = `{"apiVersion":"backstage.io/v1alpha1","kind":"User","metadata":{"name":"jdoe"},"spec":{"profile":{"displayName":"Jenny Doe","email":"jenny-doe@example.com","picture":"https://example.com/staff/jenny-with-party-hat.jpeg"},"memberOf":["team-b","employees"]}}`
var entity Entity
assert.NilError(t, json.Unmarshal([]byte(component), &entity))
expected := &UserSpec{
Profile: Profile{
DisplayName: "Jenny Doe",
Email: "jenny-doe@example.com",
Picture: "https://example.com/staff/jenny-with-party-hat.jpeg",
},
MemberOf: []string{"team-b", "employees"},
}
actual, err := entity.UserSpec()
assert.NilError(t, err)
assert.DeepEqual(t, expected, actual)
}
2 changes: 2 additions & 0 deletions cmd/backstage/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ github.com/adrg/xdg v0.4.0/go.mod h1:N6ag73EX4wyxeaoeHctc1mas01KZgsj5tYiAIwqJE/E
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
Expand All @@ -21,3 +22,4 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o=
4 changes: 4 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
module go.einride.tech/backstage

go 1.20

require gotest.tools/v3 v3.4.0

require github.com/google/go-cmp v0.5.5 // indirect
28 changes: 28 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o=
gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g=

0 comments on commit 681e3a0

Please sign in to comment.