fix: filter building (#17)

- update filter building for `dryRunValidation` to append with `AND`
- Add test coverage for filter building and dryRunValidation
This commit is contained in:
Elim Tsiagbey authored and GitHub committed 2024-11-06 12:43:06 -05:00
1 parent 0d5cc5d444
commit 7076cd0bad
3 files changed
+309 -4

No files matched your search

+226 -1
View File
@@ -663,7 +663,7 @@ func TestListHosts(t *testing.T) {
},
},
},
expectedErr: "error building filters: filter \"not-a-filter\" contains invalid operator",
expectedErr: "error building filters: invalid filter \"not-a-filter\"",
expectedErrCode: codes.InvalidArgument,
},
}
@@ -2191,3 +2191,228 @@ func TestNormalizeSetData(t *testing.T) {
})
}
}
func TestBuildFilters(t *testing.T) {
cases := []struct {
name string
attrs *SetAttributes
expected []string
expectedErr string
}{
{
name: "no filters provided",
attrs: &SetAttributes{
Filters: []string{},
},
expected: []string{defaultStatusFilter},
},
{
name: "valid filters provided",
attrs: &SetAttributes{
Filters: []string{"name = instance-1", "zone = us-central1-a"},
},
expected: []string{"name = instance-1", "zone = us-central1-a", defaultStatusFilter},
},
{
name: "filter with invalid operator",
attrs: &SetAttributes{
Filters: []string{"name ~ instance-1"},
},
expectedErr: "invalid filter \"name ~ instance-1\"",
},
{
name: "filter with empty key",
attrs: &SetAttributes{
Filters: []string{" = instance-1"},
},
expectedErr: "invalid filter \" = instance-1\"",
},
{
name: "filter with empty value",
attrs: &SetAttributes{
Filters: []string{"name = "},
},
expectedErr: "filter \"name = \" contains an empty value",
},
{
name: "filter with status key",
attrs: &SetAttributes{
Filters: []string{"status = terminated"},
},
expected: []string{"status = terminated"},
},
{
name: "filter with multiple filters",
attrs: &SetAttributes{
Filters: []string{"status = terminated", "name = instance-1"},
},
expected: []string{"status = terminated", "name = instance-1"},
},
}
for _, tc := range cases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
require := require.New(t)
actual, err := buildFilters(tc.attrs)
if tc.expectedErr != "" {
require.Contains(err.Error(), tc.expectedErr)
return
}
require.NoError(err)
require.Equal(tc.expected, actual)
})
}
}
func TestDryRunValidation(t *testing.T) {
ctx := context.Background()
cases := []struct {
name string
state *gcpCatalogPersistedState
listFilters []string
clientOptions []option.ClientOption
expectedErr string
expectedErrCode codes.Code
}{
{
name: "nil state",
state: nil,
expectedErr: "persisted state is required",
expectedErrCode: codes.InvalidArgument,
},
{
name: "error getting instances client",
state: &gcpCatalogPersistedState{
PersistedState: &cred.PersistedState{
CredentialsConfig: &cred.Config{
ProjectId: "test-project",
Zone: "us-central1-a",
},
},
testInstancesAPIFunc: newTestMockInstances(ctx,
nil,
testMockInstancesWithListInstancesOutput(nil),
testMockInstancesWithListInstancesError(fmt.Errorf("failed to list instances")),
),
},
clientOptions: []option.ClientOption{
option.WithHTTPClient(&http.Client{}),
},
expectedErr: "gcp list instances failed",
expectedErrCode: codes.FailedPrecondition,
},
{
name: "list instances error",
state: &gcpCatalogPersistedState{
PersistedState: &cred.PersistedState{
CredentialsConfig: &cred.Config{
ProjectId: "test-project",
Zone: "us-central1-a",
},
},
testInstancesAPIFunc: newTestMockInstances(ctx,
nil,
testMockInstancesWithListInstancesOutput(nil),
testMockInstancesWithListInstancesError(fmt.Errorf("failed to list instances")),
),
},
expectedErr: "gcp list instances failed",
expectedErrCode: codes.FailedPrecondition,
},
{
name: "success with filters",
state: &gcpCatalogPersistedState{
PersistedState: &cred.PersistedState{
CredentialsConfig: &cred.Config{
ProjectId: "test-project",
Zone: "us-central1-a",
},
},
testInstancesAPIFunc: newTestMockInstances(ctx,
nil,
testMockInstancesWithListInstancesOutput(&computepb.InstanceList{
Items: []*computepb.Instance{
{
Name: pointer("boundary-1"),
Id: pointer(uint64(1)),
NetworkInterfaces: []*computepb.NetworkInterface{
{
AccessConfigs: []*computepb.AccessConfig{
{
NatIP: pointer("102.1.1.1"),
ExternalIpv6: pointer("2001:db8::1"),
},
},
Ipv6AccessConfigs: []*computepb.AccessConfig{
{
ExternalIpv6: pointer("2001:db8::1"),
},
},
},
},
},
},
}),
testMockInstancesWithListInstancesError(nil),
),
},
listFilters: []string{"name = instance-1", "zone = us-central1-a"},
},
{
name: "success without filters",
state: &gcpCatalogPersistedState{
PersistedState: &cred.PersistedState{
CredentialsConfig: &cred.Config{
ProjectId: "test-project",
Zone: "us-central1-a",
},
},
testInstancesAPIFunc: newTestMockInstances(ctx,
nil,
testMockInstancesWithListInstancesOutput(&computepb.InstanceList{
Items: []*computepb.Instance{
{
Name: pointer("boundary-1"),
Id: pointer(uint64(1)),
NetworkInterfaces: []*computepb.NetworkInterface{
{
AccessConfigs: []*computepb.AccessConfig{
{
NatIP: pointer("102.1.1.1"),
ExternalIpv6: pointer("2001:db8::1"),
},
},
Ipv6AccessConfigs: []*computepb.AccessConfig{
{
ExternalIpv6: pointer("2001:db8::1"),
},
},
},
},
},
},
}),
testMockInstancesWithListInstancesError(nil),
),
},
},
}
for _, tc := range cases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
require := require.New(t)
st := dryRunValidation(ctx, tc.state, tc.listFilters, tc.clientOptions...)
if tc.expectedErr != "" {
require.Contains(st.Err().Error(), tc.expectedErr)
require.Equal(st.Code(), tc.expectedErrCode)
return
}
require.NoError(st.Err())
})
}
}