Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions internal/config/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ import (
"strings"
)

const (
StatusNew = "new"
StatusExisting = "existing"
)

// LibrarianState defines the contract for the state.yaml file.
type LibrarianState struct {
// The name and tag of the generator image to use. tag is required.
Expand Down Expand Up @@ -189,6 +194,9 @@ type API struct {
Path string `yaml:"path" json:"path"`
// The name of the service config file, relative to the API `path`.
ServiceConfig string `yaml:"service_config" json:"service_config"`
// The status of the API, one of "new" or "existing".
// This field is ignored when writing to state.yaml.
Status string `yaml:"-" json:"status"`
}

// Validate checks that the API is valid.
Expand Down
170 changes: 97 additions & 73 deletions internal/config/state_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,16 @@
package config

import (
"strings"
"testing"
)

func TestLibrarianState_Validate(t *testing.T) {
for _, test := range []struct {
name string
state *LibrarianState
wantErr bool
name string
state *LibrarianState
wantErr bool
wantErrMsg string
}{
{
name: "valid state",
Expand Down Expand Up @@ -56,29 +58,44 @@ func TestLibrarianState_Validate(t *testing.T) {
},
},
},
wantErr: true,
wantErr: true,
wantErrMsg: "image is required",
},
{
name: "missing libraries",
state: &LibrarianState{
Image: "gcr.io/test/image:v1.2.3",
},
wantErr: true,
wantErr: true,
wantErrMsg: "libraries cannot be empty",
},
} {
t.Run(test.name, func(t *testing.T) {
if err := test.state.Validate(); (err != nil) != test.wantErr {
t.Errorf("LibrarianState.Validate() error = %v, wantErr %v", err, test.wantErr)
err := test.state.Validate()
if test.wantErr {
if err == nil {
t.Error("Librarian.Validate() should fail")
}
if !strings.Contains(err.Error(), test.wantErrMsg) {
t.Errorf("want error message %q, got %q", test.wantErrMsg, err.Error())
}

return
}

if err != nil {
t.Errorf("Librarian.Validate() error = %v, wantErr %v", err, test.wantErr)
}
})
}
}

func TestLibrary_Validate(t *testing.T) {
for _, test := range []struct {
name string
library *LibraryState
wantErr bool
name string
library *LibraryState
wantErr bool
wantErrMsg string
}{
{
name: "valid library",
Expand All @@ -93,42 +110,26 @@ func TestLibrary_Validate(t *testing.T) {
},
},
{
name: "missing id",
library: &LibraryState{
SourceRoots: []string{"src/a", "src/b"},
APIs: []*API{
{
Path: "a/b/v1",
},
},
},
wantErr: true,
name: "missing id",
library: &LibraryState{},
wantErr: true,
wantErrMsg: "id is required",
},
{
name: "id is dot",
library: &LibraryState{
ID: ".",
SourceRoots: []string{"src/a", "src/b"},
APIs: []*API{
{
Path: "a/b/v1",
},
},
ID: ".",
},
wantErr: true,
wantErr: true,
wantErrMsg: "id cannot be",
},
{
name: "id is double dot",
library: &LibraryState{
ID: "..",
SourceRoots: []string{"src/a", "src/b"},
APIs: []*API{
{
Path: "a/b/v1",
},
},
ID: "..",
},
wantErr: true,
wantErr: true,
wantErrMsg: "id cannot be",
},
{
name: "missing source paths",
Expand All @@ -140,15 +141,17 @@ func TestLibrary_Validate(t *testing.T) {
},
},
},
wantErr: true,
wantErr: true,
wantErrMsg: "source_roots cannot be empty",
},
{
name: "missing apis",
library: &LibraryState{
ID: "a/b",
SourceRoots: []string{"src/a", "src/b"},
},
wantErr: true,
wantErr: true,
wantErrMsg: "apis cannot be empty",
},
{
name: "valid version without v prefix",
Expand All @@ -166,43 +169,28 @@ func TestLibrary_Validate(t *testing.T) {
{
name: "invalid id characters",
library: &LibraryState{
ID: "a/b!",
SourceRoots: []string{"src/a", "src/b"},
APIs: []*API{
{
Path: "a/b/v1",
},
},
ID: "a/b!",
},
wantErr: true,
wantErr: true,
wantErrMsg: "invalid id",
},
{
name: "invalid last generated commit non-hex",
library: &LibraryState{
ID: "a/b",
LastGeneratedCommit: "not-a-hex-string",
SourceRoots: []string{"src/a", "src/b"},
APIs: []*API{
{
Path: "a/b/v1",
},
},
},
wantErr: true,
wantErr: true,
wantErrMsg: "last_generated_commit must be a hex string",
},
{
name: "invalid last generated commit wrong length",
library: &LibraryState{
ID: "a/b",
LastGeneratedCommit: "deadbeef",
SourceRoots: []string{"src/a", "src/b"},
APIs: []*API{
{
Path: "a/b/v1",
},
},
},
wantErr: true,
wantErr: true,
wantErrMsg: "last_generated_commit must be 40 characters",
},
{
name: "valid preserve_regex",
Expand All @@ -221,7 +209,8 @@ func TestLibrary_Validate(t *testing.T) {
APIs: []*API{{Path: "a/b/v1"}},
PreserveRegex: []string{"["},
},
wantErr: true,
wantErr: true,
wantErrMsg: "invalid preserve_regex at index",
},
{
name: "valid remove_regex",
Expand All @@ -240,7 +229,8 @@ func TestLibrary_Validate(t *testing.T) {
APIs: []*API{{Path: "a/b/v1"}},
RemoveRegex: []string{"("},
},
wantErr: true,
wantErr: true,
wantErrMsg: "invalid remove_regex at index",
},
{
name: "valid release_exclude_path",
Expand All @@ -259,7 +249,8 @@ func TestLibrary_Validate(t *testing.T) {
APIs: []*API{{Path: "a/b/v1"}},
ReleaseExcludePaths: []string{"/a/b"},
},
wantErr: true,
wantErr: true,
wantErrMsg: "invalid release_exclude_path at index",
},
{
name: "valid tag_format",
Expand All @@ -278,11 +269,24 @@ func TestLibrary_Validate(t *testing.T) {
APIs: []*API{{Path: "a/b/v1"}},
TagFormat: "{id}-{foo}",
},
wantErr: true,
wantErr: true,
wantErrMsg: "invalid placeholder in tag_format",
},
} {
t.Run(test.name, func(t *testing.T) {
if err := test.library.Validate(); (err != nil) != test.wantErr {
err := test.library.Validate()
if test.wantErr {
if err == nil {
t.Error("Library.Validate() should fail")
}
if !strings.Contains(err.Error(), test.wantErrMsg) {
t.Errorf("want error message %q, got %q", test.wantErrMsg, err.Error())
}

return
}

if err != nil {
t.Errorf("Library.Validate() error = %v, wantErr %v", err, test.wantErr)
}
})
Expand All @@ -291,24 +295,44 @@ func TestLibrary_Validate(t *testing.T) {

func TestAPI_Validate(t *testing.T) {
for _, test := range []struct {
name string
api *API
wantErr bool
name string
api *API
wantErr bool
wantErrMsg string
}{
{
name: "valid api",
name: "new api",
api: &API{
Path: "a/b/v1",
},
},
{
name: "existing api",
api: &API{
Path: "a/b/v1",
},
},
{
name: "missing path",
api: &API{},
wantErr: true,
name: "missing path",
api: &API{},
wantErr: true,
wantErrMsg: "invalid path",
},
} {
t.Run(test.name, func(t *testing.T) {
if err := test.api.Validate(); (err != nil) != test.wantErr {
err := test.api.Validate()
if test.wantErr {
if err == nil {
t.Error("API.Validate() should fail")
}
if !strings.Contains(err.Error(), test.wantErrMsg) {
t.Errorf("want error message %q, got %q", test.wantErrMsg, err.Error())
}

return
}

if err != nil {
t.Errorf("API.Validate() error = %v, wantErr %v", err, test.wantErr)
}
})
Expand Down
2 changes: 1 addition & 1 deletion internal/docker/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ func (c *Docker) Build(ctx context.Context, request *BuildRequest) error {
// Returns the configured library id if the command succeeds.
func (c *Docker) Configure(ctx context.Context, request *ConfigureRequest) (string, error) {
requestFilePath := filepath.Join(request.RepoDir, config.LibrarianDir, config.ConfigureRequest)
if err := writeLibraryState(request.State, request.LibraryID, requestFilePath); err != nil {
if err := writeLibrarianState(request.State, requestFilePath); err != nil {
return "", err
}
defer func() {
Expand Down
4 changes: 4 additions & 0 deletions internal/docker/docker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -820,6 +820,7 @@ func TestWriteLibraryState(t *testing.T) {
{
Path: "google/cloud/compute/v1",
ServiceConfig: "example_service_config.yaml",
Status: "new",
},
},
SourceRoots: []string{
Expand All @@ -839,6 +840,7 @@ func TestWriteLibraryState(t *testing.T) {
{
Path: "google/storage/v1",
ServiceConfig: "storage_service_config.yaml",
Status: "existing",
},
},
},
Expand Down Expand Up @@ -1048,6 +1050,7 @@ func TestWriteLibrarianState(t *testing.T) {
{
Path: "google/cloud/compute/v1",
ServiceConfig: "example_service_config.yaml",
Status: "existing",
},
},
SourceRoots: []string{
Expand All @@ -1067,6 +1070,7 @@ func TestWriteLibrarianState(t *testing.T) {
{
Path: "google/storage/v1",
ServiceConfig: "storage_service_config.yaml",
Status: "existing",
},
},
},
Expand Down
13 changes: 11 additions & 2 deletions internal/librarian/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -515,10 +515,11 @@ func (r *generateRunner) runConfigureCommand(ctx context.Context) (string, error
return "", err
}

// record to state, not write to state.yaml
setAllAPIStatus(r.state, config.StatusExisting)
// Record to state, not write to state.yaml
r.state.Libraries = append(r.state.Libraries, &config.LibraryState{
ID: r.cfg.Library,
APIs: []*config.API{{Path: r.cfg.API}},
APIs: []*config.API{{Path: r.cfg.API, Status: config.StatusNew}},
})

if err := populateServiceConfigIfEmpty(
Expand Down Expand Up @@ -562,3 +563,11 @@ func (r *generateRunner) runConfigureCommand(ctx context.Context) (string, error

return libraryState.ID, nil
}

func setAllAPIStatus(state *config.LibrarianState, status string) {
for _, library := range state.Libraries {
for _, api := range library.APIs {
api.Status = status
}
}
}
Loading