278 lines
6.9 KiB
Go
278 lines
6.9 KiB
Go
package state
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
)
|
|
|
|
func TestSelectReleasesWithOverrides(t *testing.T) {
|
|
type testcase struct {
|
|
subject string
|
|
selector []string
|
|
want []string
|
|
}
|
|
|
|
testcases := []testcase{
|
|
{
|
|
subject: "multiple OR selectors (nillable label first)",
|
|
selector: []string{"type=bar", "name=nolabel2", "name=nolabel1"},
|
|
want: []string{"nolabel1", "nolabel2", "foo"},
|
|
},
|
|
{
|
|
subject: "multiple OR selectors (non-nillable label first)",
|
|
selector: []string{"name=foo", "type!=bar"},
|
|
want: []string{"nolabel1", "nolabel2", "foo", "bar", "foobar"},
|
|
},
|
|
{
|
|
subject: "multiple AND conditions (nillable label first)",
|
|
selector: []string{"type!=bar,name!=nolabel2"},
|
|
want: []string{"nolabel1", "bar", "foobar"},
|
|
},
|
|
{
|
|
subject: "multiple AND conditions (non-nillable label first)",
|
|
selector: []string{"name!=nolabel2,type!=bar"},
|
|
want: []string{"nolabel1", "bar", "foobar"},
|
|
},
|
|
{
|
|
subject: "inequality on nillable label",
|
|
selector: []string{"type!=bar"},
|
|
want: []string{"nolabel1", "nolabel2", "bar", "foobar"},
|
|
},
|
|
{
|
|
subject: "equality on nillable label",
|
|
selector: []string{"type=bar"},
|
|
want: []string{"foo"},
|
|
},
|
|
{
|
|
subject: "inequality on non-nillable label",
|
|
selector: []string{"name!=nolabel1"},
|
|
want: []string{"nolabel2", "foo", "bar", "foobar"},
|
|
},
|
|
{
|
|
subject: "equality on non-nillable label",
|
|
selector: []string{"name=nolabel1"},
|
|
want: []string{"nolabel1"},
|
|
},
|
|
{
|
|
subject: "equality on non-alphabetic character label",
|
|
selector: []string{"version=1.2/3"},
|
|
want: []string{"bar"},
|
|
},
|
|
{
|
|
subject: "equality on non-alphabetic character label",
|
|
selector: []string{"version=1.2.3+123"},
|
|
want: []string{"foobar"},
|
|
},
|
|
{
|
|
subject: "inequality on non-alphatetic character label",
|
|
selector: []string{"version!=1.2/3"},
|
|
want: []string{"nolabel1", "nolabel2", "foo", "foobar"},
|
|
},
|
|
{
|
|
subject: "inequality on non-alphatetic character label",
|
|
selector: []string{"version!=1.2.3+123"},
|
|
want: []string{"nolabel1", "nolabel2", "foo", "bar"},
|
|
},
|
|
}
|
|
|
|
example := []byte(`releases:
|
|
- name: nolabel1
|
|
namespace: kube-system
|
|
chart: stable/nolabel
|
|
- name: nolabel2
|
|
namespace: default
|
|
chart: stable/nolabel
|
|
- name: foo
|
|
namespace: kube-system
|
|
chart: stable/foo
|
|
labels:
|
|
type: bar
|
|
- name: bar
|
|
namespace: kube-system
|
|
chart: stable/bar
|
|
labels:
|
|
version: 1.2/3
|
|
- name: foobar
|
|
namespace: kube-system
|
|
chart: stable/bar
|
|
labels:
|
|
version: 1.2.3+123
|
|
`)
|
|
|
|
state := stateTestEnv{
|
|
Files: map[string]string{
|
|
"/helmfile.yaml": string(example),
|
|
},
|
|
WorkDir: "/",
|
|
}.MustLoadState(t, "/helmfile.yaml", "default")
|
|
|
|
for _, tc := range testcases {
|
|
var err error
|
|
state.Selectors = tc.selector
|
|
|
|
state.Releases, err = state.GetReleasesWithOverrides()
|
|
if err != nil {
|
|
t.Fatalf("%s %s: %v", tc.selector, tc.subject, err)
|
|
}
|
|
state.Releases = state.GetReleasesWithLabels()
|
|
|
|
rs, err := state.GetSelectedReleases(false, false)
|
|
if err != nil {
|
|
t.Fatalf("%s %s: %v", tc.selector, tc.subject, err)
|
|
}
|
|
|
|
var got []string
|
|
|
|
for _, r := range rs {
|
|
got = append(got, r.Name)
|
|
}
|
|
|
|
if d := cmp.Diff(tc.want, got); d != "" {
|
|
t.Errorf("%s %s: %s", tc.selector, tc.subject, d)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestSelectReleasesWithOverridesWithIncludedTransitives(t *testing.T) {
|
|
type testcase struct {
|
|
subject string
|
|
selector []string
|
|
want []string
|
|
includeNeeds bool
|
|
includeTransitiveNeeds bool
|
|
}
|
|
|
|
testcases := []testcase{
|
|
{
|
|
subject: "no needs inclusion",
|
|
selector: []string{"name=serviceA"},
|
|
want: []string{"serviceA"},
|
|
includeNeeds: false,
|
|
includeTransitiveNeeds: false,
|
|
},
|
|
{
|
|
subject: "include direct needs only",
|
|
selector: []string{"name=serviceA"},
|
|
want: []string{"serviceA", "serviceB"},
|
|
includeNeeds: true,
|
|
includeTransitiveNeeds: false,
|
|
},
|
|
{
|
|
subject: "include transitive needs",
|
|
selector: []string{"name=serviceA"},
|
|
want: []string{"serviceA", "serviceB", "serviceC"},
|
|
includeNeeds: false,
|
|
includeTransitiveNeeds: true,
|
|
},
|
|
{
|
|
subject: "include both direct and transitive needs",
|
|
selector: []string{"name=serviceA"},
|
|
want: []string{"serviceA", "serviceB", "serviceC"},
|
|
includeNeeds: true,
|
|
includeTransitiveNeeds: true,
|
|
},
|
|
}
|
|
|
|
example := []byte(`releases:
|
|
- name: serviceA
|
|
namespace: default
|
|
chart: stable/testchart
|
|
needs:
|
|
- serviceB
|
|
- name: serviceB
|
|
namespace: default
|
|
chart: stable/testchart
|
|
needs:
|
|
- serviceC
|
|
- name: serviceC
|
|
namespace: default
|
|
chart: stable/testchart
|
|
- name: serviceD
|
|
namespace: default
|
|
chart: stable/testchart
|
|
`)
|
|
|
|
state := stateTestEnv{
|
|
Files: map[string]string{
|
|
"/helmfile.yaml": string(example),
|
|
},
|
|
WorkDir: "/",
|
|
}.MustLoadState(t, "/helmfile.yaml", "default")
|
|
|
|
for _, tc := range testcases {
|
|
var err error
|
|
state.Selectors = tc.selector
|
|
state.Releases, err = state.GetReleasesWithOverrides()
|
|
if err != nil {
|
|
t.Fatalf("%s %s: %v", tc.selector, tc.subject, err)
|
|
}
|
|
state.Releases = state.GetReleasesWithLabels()
|
|
|
|
rs, err := state.GetSelectedReleases(tc.includeNeeds, tc.includeTransitiveNeeds)
|
|
if err != nil {
|
|
t.Fatalf("%s %s: %v", tc.selector, tc.subject, err)
|
|
}
|
|
|
|
var got []string
|
|
|
|
for _, r := range rs {
|
|
got = append(got, r.Name)
|
|
}
|
|
|
|
if d := cmp.Diff(tc.want, got); d != "" {
|
|
t.Errorf("%s %s: %s", tc.selector, tc.subject, d)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestSelectReleasesWithIncludeNeedsCrossNamespace(t *testing.T) {
|
|
// When multiple releases share the same name across different namespaces,
|
|
// includeNeeds should resolve the correct dependency using fully-qualified IDs
|
|
// rather than name-based lookup (which would be ambiguous).
|
|
example := []byte(`releases:
|
|
- name: frontend
|
|
namespace: team-a
|
|
chart: stable/testchart
|
|
needs:
|
|
- team-a/backend
|
|
- name: backend
|
|
namespace: team-a
|
|
chart: stable/testchart
|
|
- name: backend
|
|
namespace: team-b
|
|
chart: stable/testchart
|
|
`)
|
|
|
|
state := stateTestEnv{
|
|
Files: map[string]string{
|
|
"/helmfile.yaml": string(example),
|
|
},
|
|
WorkDir: "/",
|
|
}.MustLoadState(t, "/helmfile.yaml", "default")
|
|
|
|
state.Selectors = []string{"name=frontend"}
|
|
var err error
|
|
state.Releases, err = state.GetReleasesWithOverrides()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
state.Releases = state.GetReleasesWithLabels()
|
|
|
|
rs, err := state.GetSelectedReleases(true, false)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
var got []string
|
|
for _, r := range rs {
|
|
got = append(got, r.Namespace+"/"+r.Name)
|
|
}
|
|
|
|
// Should include team-a/backend (direct need) but NOT team-b/backend
|
|
want := []string{"team-a/frontend", "team-a/backend"}
|
|
if d := cmp.Diff(want, got); d != "" {
|
|
t.Errorf("cross-namespace include-needs: %s", d)
|
|
}
|
|
}
|