From 7cc358fc3a8e533a128102dc5020dc134ce32579 Mon Sep 17 00:00:00 2001 From: unclegedd Date: Wed, 27 Mar 2024 14:59:35 -0500 Subject: [PATCH 1/4] chore: adds TUI tests --- src/pkg/bundle/tui/deploy/model.go | 3 + src/pkg/bundle/tui/deploy/model_test.go | 114 ++++++++++++++++++++++++ src/test/e2e/commands_test.go | 11 ++- src/test/e2e/tui_test.go | 58 ++++++++++++ 4 files changed, 184 insertions(+), 2 deletions(-) create mode 100644 src/pkg/bundle/tui/deploy/model_test.go create mode 100644 src/test/e2e/tui_test.go diff --git a/src/pkg/bundle/tui/deploy/model.go b/src/pkg/bundle/tui/deploy/model.go index da92b8e3..9225d26c 100644 --- a/src/pkg/bundle/tui/deploy/model.go +++ b/src/pkg/bundle/tui/deploy/model.go @@ -247,6 +247,9 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { if tc, err := strconv.Atoi(strings.Split(msg, ":")[1]); err == nil { m.packages[m.pkgIdx].numComponents = tc m.packages[m.pkgIdx].componentStatuses = make([]bool, tc) + if m.isRemoteBundle { + m.packages[m.pkgIdx].downloaded = true + } } case totalPackages: if totalPkgs, err := strconv.Atoi(strings.Split(msg, ":")[1]); err == nil { diff --git a/src/pkg/bundle/tui/deploy/model_test.go b/src/pkg/bundle/tui/deploy/model_test.go new file mode 100644 index 00000000..92b00d53 --- /dev/null +++ b/src/pkg/bundle/tui/deploy/model_test.go @@ -0,0 +1,114 @@ +package deploy + +import ( + "testing" + + tea "github.com/charmbracelet/bubbletea" + "github.com/stretchr/testify/require" +) + +func TestDeploy(t *testing.T) { + testPkgs := []pkgState{ + { + name: "test-pkg", + numComponents: 1, + componentStatuses: []bool{true}, + }, { + name: "test-pkg-2", + numComponents: 1, + componentStatuses: []bool{true}, + }, + } + initTestModel := func() *Model { + + m := InitModel(nil) + m.validatingBundle = false + m.totalPkgs = 2 + m.bundleName = "test-bundle" + m.logViewport.Width = 50 + m.logViewport.Height = 50 + m.bundleYAML = "fake bundle YAML" + return &m + } + + t.Run("test deploy", func(t *testing.T) { + m := initTestModel() + + // check pre-deploy view + view := m.View() + require.Contains(t, view, m.bundleYAML) + require.Contains(t, view, "Deploy this bundle? (y/n)") + + // simulate pressing 'y' key to confirm deployment + m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []int32{121}}) + view = m.View() + require.Contains(t, view, "UDS Bundle: test-bundle") + + // deploy first pkg in bundle with simulated components + m.Update("newPackage:test-pkg:0") + m.packages[m.pkgIdx].numComponents = 1 + m.packages[m.pkgIdx].componentStatuses = []bool{true} + view = m.View() + require.Contains(t, view, "Deploying bundle package (1 / 2)") + require.Contains(t, view, "Package test-pkg deploying (1 / 1 components)") + + // simulate package deployment completion + m.Update("complete:test-pkg") + //m.Update(deployTickMsg(time.Time{})) + view = m.View() + require.Contains(t, view, "Package test-pkg deployed") + require.NotContains(t, view, "Package test-pkg deploying") + + // deploy second pkg in bundle with simulated components + m.Update("newPackage:test-pkg-2:1") + m.packages[m.pkgIdx].numComponents = 1 + m.packages[m.pkgIdx].componentStatuses = []bool{true} + view = m.View() + require.Contains(t, view, "Deploying bundle package (2 / 2)") + require.Contains(t, view, "Package test-pkg-2 deploying (1 / 1 components)") + + // simulate package deployment completion + m.Update("complete:test-pkg-2") + view = m.View() + require.Contains(t, view, "Package test-pkg-2 deployed") + require.NotContains(t, view, "Package test-pkg-2 deploying") + }) + + t.Run("test toggle log view", func(t *testing.T) { + m := initTestModel() + + // simulate passing --confirm + m.inProgress = true + m.confirmed = true + m.packages = testPkgs + + view := m.View() + require.Contains(t, view, "Package test-pkg deploying (1 / 1 components)") + + // simulate pressing 'l' key to toggle logs + m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []int32{108}}) + + view = m.View() + require.Contains(t, view, "test-pkg package logs") + + // simulate pressing 'l' key to toggle logs + m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []int32{108}}) + + view = m.View() + require.NotContains(t, view, "test-pkg package logs") + }) + + t.Run("test deploy cancel", func(t *testing.T) { + m := initTestModel() + view := m.View() + require.Contains(t, view, "Deploy this bundle? (y/n)") + + // simulate pressing 'n' key to cancel deployment + m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []int32{110}}) + view = m.View() + + // model's view is cleared after canceling deployment + require.Equal(t, view, "") + }) + +} diff --git a/src/test/e2e/commands_test.go b/src/test/e2e/commands_test.go index 72adfa44..c5209da3 100644 --- a/src/test/e2e/commands_test.go +++ b/src/test/e2e/commands_test.go @@ -105,6 +105,13 @@ func deploy(t *testing.T, tarballPath string) (stdout string, stderr string) { return stdout, stderr } +func deployWithTUI(t *testing.T, source string) (stdout string, stderr string) { + cmd := strings.Split(fmt.Sprintf("deploy %s --confirm", source), " ") + stdout, stderr, err := e2e.UDS(cmd...) + require.NoError(t, err) + return stdout, stderr +} + func runCmd(t *testing.T, input string) (stdout string, stderr string) { cmd := strings.Split(input, " ") stdout, stderr, err := e2e.UDS(cmd...) @@ -124,8 +131,8 @@ func deployResumeFlag(t *testing.T, tarballPath string) { require.NoError(t, err) } -func remove(t *testing.T, tarballPath string) { - cmd := strings.Split(fmt.Sprintf("remove %s --confirm --insecure", tarballPath), " ") +func remove(t *testing.T, source string) { + cmd := strings.Split(fmt.Sprintf("remove %s --confirm --insecure", source), " ") _, _, err := e2e.UDS(cmd...) require.NoError(t, err) } diff --git a/src/test/e2e/tui_test.go b/src/test/e2e/tui_test.go new file mode 100644 index 00000000..93a4b7bc --- /dev/null +++ b/src/test/e2e/tui_test.go @@ -0,0 +1,58 @@ +package test + +import ( + "fmt" + "path/filepath" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestBundleDeploy(t *testing.T) { + deployZarfInit(t) + e2e.CreateZarfPkg(t, "src/test/packages/podinfo", false) + + source := "ghcr.io/defenseunicorns/packages/uds-cli/test/publish/ghcr-test:0.0.1" + stdout, _ := deployWithTUI(t, source) + require.Contains(t, stdout, "Validating bundle") + require.Contains(t, stdout, "UDS Bundle: ghcr-test") + require.Contains(t, stdout, "Verifying podinfo package (0%)") + require.Contains(t, stdout, "Downloading podinfo package (0%)") + require.Contains(t, stdout, "Deploying podinfo package (1 / 1 components)") + require.Contains(t, stdout, "Deploying nginx package (1 / 1 components)") + require.Contains(t, stdout, "✔ Package podinfo deployed") + require.Contains(t, stdout, "✔ Package nginx deployed") + require.Contains(t, stdout, "Verifying nginx package (0%)") + require.Contains(t, stdout, "Downloading nginx package (0%)") + require.Contains(t, stdout, "✨ Bundle ghcr-test deployed successfully") + remove(t, source) +} + +func TestBundleDeployWithBadSource(t *testing.T) { + deployZarfInit(t) + e2e.CreateZarfPkg(t, "src/test/packages/podinfo", false) + + source := "a.bad.source" + stdout, _ := deployWithTUI(t, source) + require.Contains(t, stdout, "❌ Error deploying bundle: a.bad.source: not found") + remove(t, source) +} + +func TestBundleDeployWithBadPkg(t *testing.T) { + deployZarfInit(t) + + // deploy a good pkg + source := "ghcr.io/defenseunicorns/packages/uds-cli/test/publish/ghcr-test:0.0.1 --packages=nginx" + stdout, _ := deployWithTUI(t, source) + require.Contains(t, stdout, "✨ Bundle ghcr-test deployed successfully") + + // attempt to deploy a conflicting pkg + e2e.CreateZarfPkg(t, "src/test/packages/gitrepo", false) + bundleDir := "src/test/bundles/05-gitrepo" + bundlePath := filepath.Join(bundleDir, fmt.Sprintf("uds-bundle-gitrepo-%s-0.0.1.tar.zst", e2e.Arch)) + + createLocal(t, bundleDir, e2e.Arch) + stdout, _ = deployWithTUI(t, bundlePath) + require.Contains(t, stdout, "❌ Error deploying bundle: unable to deploy component \"nginx-remote\": unable to install helm chart") + remove(t, source) +} From 6b54768fdf2483ff393c9767f8833da95ce6d735 Mon Sep 17 00:00:00 2001 From: unclegedd Date: Wed, 27 Mar 2024 15:08:41 -0500 Subject: [PATCH 2/4] adds one more check --- src/test/e2e/tui_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/e2e/tui_test.go b/src/test/e2e/tui_test.go index 93a4b7bc..0661e267 100644 --- a/src/test/e2e/tui_test.go +++ b/src/test/e2e/tui_test.go @@ -54,5 +54,6 @@ func TestBundleDeployWithBadPkg(t *testing.T) { createLocal(t, bundleDir, e2e.Arch) stdout, _ = deployWithTUI(t, bundlePath) require.Contains(t, stdout, "❌ Error deploying bundle: unable to deploy component \"nginx-remote\": unable to install helm chart") + require.Contains(t, stdout, "Run uds logs to view deployment logs") remove(t, source) } From e25efef621672d02d26f5048fae8c4555221e353 Mon Sep 17 00:00:00 2001 From: unclegedd Date: Wed, 27 Mar 2024 15:20:19 -0500 Subject: [PATCH 3/4] addresses Github linter findings --- src/pkg/bundle/deploy.go | 1 + src/pkg/bundle/tarball.go | 2 +- src/pkg/bundle/tui/deploy/model.go | 4 ++++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/pkg/bundle/deploy.go b/src/pkg/bundle/deploy.go index c2b83841..006b7dd1 100644 --- a/src/pkg/bundle/deploy.go +++ b/src/pkg/bundle/deploy.go @@ -281,6 +281,7 @@ func (b *Bundle) loadChartOverrides(pkg types.Package, pkgVars map[string]string return processed, nil } +// PreDeployValidation validates the bundle before deployment func (b *Bundle) PreDeployValidation() (string, string, string, error) { // Check that provided oci source path is valid, and update it if it's missing the full path diff --git a/src/pkg/bundle/tarball.go b/src/pkg/bundle/tarball.go index 5fbbca57..1213dfd6 100644 --- a/src/pkg/bundle/tarball.go +++ b/src/pkg/bundle/tarball.go @@ -301,7 +301,7 @@ func (tp *tarballBundleProvider) PublishBundle(bundle types.UDSBundle, remote *o retries := 0 // reset retries if a desc was successful - copyOpts.PostCopy = func(_ context.Context, desc ocispec.Descriptor) error { + copyOpts.PostCopy = func(_ context.Context, _ ocispec.Descriptor) error { retries = 0 return nil } diff --git a/src/pkg/bundle/tui/deploy/model.go b/src/pkg/bundle/tui/deploy/model.go index 9225d26c..50835ee3 100644 --- a/src/pkg/bundle/tui/deploy/model.go +++ b/src/pkg/bundle/tui/deploy/model.go @@ -67,6 +67,7 @@ type pkgState struct { isRemote bool } +// Model contains the state of the TUI type Model struct { bndlClient bndlClientShim bundleYAML string @@ -137,12 +138,14 @@ func InitModel(client bndlClientShim) Model { } } +// Init performs some action when BubbleTea starts up func (m *Model) Init() tea.Cmd { return func() tea.Msg { return doPreDeploy } } +// Update updates the model based on the message received func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { select { case err := <-m.errChan: @@ -279,6 +282,7 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m, nil } +// View returns the view for the TUI func (m *Model) View() string { if m.done { // no errors, clear the controlled Program's output From a55ae7cf0b923943bb9bb51dbc4c1c1971f472a5 Mon Sep 17 00:00:00 2001 From: unclegedd Date: Wed, 27 Mar 2024 15:25:35 -0500 Subject: [PATCH 4/4] fixing test --- src/test/e2e/tui_test.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/test/e2e/tui_test.go b/src/test/e2e/tui_test.go index 0661e267..fe13093e 100644 --- a/src/test/e2e/tui_test.go +++ b/src/test/e2e/tui_test.go @@ -32,10 +32,9 @@ func TestBundleDeployWithBadSource(t *testing.T) { deployZarfInit(t) e2e.CreateZarfPkg(t, "src/test/packages/podinfo", false) - source := "a.bad.source" + source := "a.bad.source:0.0.1" stdout, _ := deployWithTUI(t, source) - require.Contains(t, stdout, "❌ Error deploying bundle: a.bad.source: not found") - remove(t, source) + require.Contains(t, stdout, "❌ Error deploying bundle: a.bad.source:0.0.1: not found") } func TestBundleDeployWithBadPkg(t *testing.T) {