diff --git a/.github/workflows/coverage-upload.yml b/.github/workflows/coverage-upload.yml new file mode 100644 index 0000000..0ea36f0 --- /dev/null +++ b/.github/workflows/coverage-upload.yml @@ -0,0 +1,55 @@ +name: Coverage Upload + +on: + workflow_run: + workflows: [testing] + types: + - completed + +jobs: + run_tests: + runs-on: ubuntu-latest + steps: + # https://github.com/actions/github-script + # Based on: https://github.com/orgs/community/discussions/34652 + - name: 'Download artifact' + uses: actions/github-script@v7 + with: + script: | + let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({ + owner: context.repo.owner, + repo: context.repo.repo, + run_id: context.payload.workflow_run.id, + }); + let matchArtifact = allArtifacts.data.artifacts.filter((artifact) => { + return artifact.name == "coverage-report" + })[0]; + let download = await github.rest.actions.downloadArtifact({ + owner: context.repo.owner, + repo: context.repo.repo, + artifact_id: matchArtifact.id, + archive_format: 'zip', + }); + let fs = require('fs'); + fs.writeFileSync(`${process.env.GITHUB_WORKSPACE}/coverage-report.zip`, Buffer.from(download.data)); + - name: 'Unzip artifact' + run: unzip coverage-report.zip + # https://github.com/actions/download-artifact + # - name: Download artifact + # id: download-artifact + # uses: actions/download-artifact@v4 + # with: + # run-id: ${{ github.event.workflow_run.id }} + # https://github.com/codacy/codacy-coverage-reporter-action + # - name: Run codacy-coverage-reporter + # uses: codacy/codacy-coverage-reporter-action@v1 + # with: + # project-token: ${{ secrets.CODACY_PROJECT_TOKEN }} + # coverage-reports: coverage.xml + - name: Publish Code Coverage Results + run: | + auth="--project-token ${{ secrets.CODACY_PROJECT_TOKEN }}" + commit_uuid="--commit-uuid ${{ github.event.workflow_run.head_sha }}" + + bash <(curl -Ls https://coverage.codacy.com/get.sh) report $auth $commit_uuid --force-coverage-parser go -r coverage.out --partial &&\ + bash <(curl -Ls https://coverage.codacy.com/get.sh) final $auth $commit_uuid \ No newline at end of file diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml new file mode 100644 index 0000000..a3fce4c --- /dev/null +++ b/.github/workflows/testing.yml @@ -0,0 +1,70 @@ +name: testing + +on: + push: + branches: + - 'main' + tags: + - 'v*' + pull_request: + +permissions: + contents: read + +jobs: + test: + name: Integration Tests (Cobbler ${{ matrix.cobbler_version }}) + runs-on: ubuntu-latest + strategy: + matrix: + cobbler_version: + # - d8f60bbf14a838c8c8a1dba98086b223e35fe70a # 3.3.0 - TypeError during import + - f5b0599acce32de4288c76e4f601aece0c664fed # 3.3.1 + # - 9044aa990a94752fa5bd5a24051adde099280bfa # 3.3.2 - Testing Docker Image broken + # - 5c498dbf2af6e3782b37605a477759e1aacc16b2 # 3.3.3 - Testing Docker Image broken + - 3ed865b79ce69fca7464e0957f4bcadcc9917a9d # 3.3.4 + - 718e3256a5989941e8a678404fdea07364255637 # 3.3.5 + - df356046f3cf27be62a61001b982d5983800cfd9 # 3.3.6 + fail-fast: false + steps: + - name: Check out code into the Go module directory + uses: actions/checkout@v4 + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: 'go.mod' + id: go + - name: Install system dependencies + run: | + sudo apt-get install -y xorriso + - name: Get dependencies + run: | + go mod download + - name: Replace git version hash + run: | + sed -i "s/cobbler_commit=.*/cobbler_commit=${{ matrix.cobbler_version }}/" testing/start.sh + - name: Restore OS ISO + id: cache-iso-restore + uses: actions/cache/restore@v4 + with: + path: | + *.iso + key: ${{ runner.os }}-${{ matrix.cobbler_version }}-iso + - name: Make Test + run: | + make test + - name: Save OS ISO + id: cache-iso-save + uses: actions/cache/save@v4 + with: + path: | + *.iso + key: ${{ steps.cache-iso-restore.outputs.cache-primary-key }} + # https://github.com/actions/upload-artifact + - name: Upload coverage report to GH artifacts + if: matrix.cobbler_version == 'df356046f3cf27be62a61001b982d5983800cfd9' + uses: actions/upload-artifact@v4 + with: + name: coverage-report + path: coverage.out + if-no-files-found: error diff --git a/.gitignore b/.gitignore index 654c4c8..96c5ae2 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,9 @@ docs/_build # goreleaser dist/ + +# Tests +testing/cobbler_source/ +extracted_iso_image/ +*.iso +coverage.out diff --git a/Makefile b/Makefile index 75d4845..916f247 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,7 @@ BINARY_NAME=cobbler EXECUTOR?=docker +COBBLER_SERVER_URL=http://localhost:8081/cobbler_api +TEST?=$$(go list ./... |grep -v 'vendor') build: @echo "building package" @@ -28,6 +30,10 @@ run: go build -o ${BINARY_NAME} main.go ./${BINARY_NAME} +test: + @./testing/start.sh ${COBBLER_SERVER_URL} + go test -v -coverprofile="coverage.out" -covermode="atomic" $(TEST) + shell_completions: @mkdir -p config/completions/bash @mkdir -p config/completions/fish diff --git a/cmd/aclsetup_test.go b/cmd/aclsetup_test.go new file mode 100644 index 0000000..cc00aac --- /dev/null +++ b/cmd/aclsetup_test.go @@ -0,0 +1,76 @@ +package cmd + +import ( + "bytes" + "fmt" + "github.com/cobbler/cobblerclient" + "github.com/spf13/cobra" + "io" + "strings" + "testing" +) + +func Test_AclSetupCmd(t *testing.T) { + type args struct { + command []string + } + tests := []struct { + name string + args args + want string + wantErr bool + }{ + { + name: "adduser", + args: args{command: []string{"--config", "../testing/.cobbler.yaml", "aclsetup", "--adduser", "cobbler"}}, + want: "Event ID:", + wantErr: false, + }, + { + name: "addgroup", + args: args{command: []string{"--config", "../testing/.cobbler.yaml", "aclsetup", "--addgroup", "cobbler"}}, + want: "Event ID:", + wantErr: false, + }, + { + name: "removeuser", + args: args{command: []string{"--config", "../testing/.cobbler.yaml", "aclsetup", "--removeuser", "cobbler"}}, + want: "Event ID:", + wantErr: false, + }, + { + name: "removegroup", + args: args{command: []string{"--config", "../testing/.cobbler.yaml", "aclsetup", "--removegroup", "cobbler"}}, + want: "Event ID:", + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Arrange + cobra.OnInitialize(initConfig, setupLogger) + rootCmd := NewRootCmd() + rootCmd.SetArgs(tt.args.command) + stdout := bytes.NewBufferString("") + stderr := bytes.NewBufferString("") + rootCmd.SetOut(stdout) + rootCmd.SetErr(stderr) + + // Act + err := rootCmd.Execute() + + // Assert + cobblerclient.FailOnError(t, err) + FailOnStdErr(t, stderr) + stdoutBytes, err := io.ReadAll(stdout) + if err != nil { + t.Fatal(err) + } + stdoutString := string(stdoutBytes) + if !strings.Contains(stdoutString, tt.want) { + fmt.Println(stdoutString) + t.Fatal("No Event ID present") + } + }) + } +} diff --git a/cmd/buildiso_test.go b/cmd/buildiso_test.go new file mode 100644 index 0000000..1d619dd --- /dev/null +++ b/cmd/buildiso_test.go @@ -0,0 +1 @@ +package cmd diff --git a/cmd/distro_test.go b/cmd/distro_test.go new file mode 100644 index 0000000..043c55d --- /dev/null +++ b/cmd/distro_test.go @@ -0,0 +1,10 @@ +package cmd + +// TODO: distro report +// TODO: distro report --name=test +// TODO: distro edit +// TODO: distro find +// TODO: distro copy +// TODO: distro rename +// TODO: distro add +// TODO: distro remove diff --git a/cmd/event_test.go b/cmd/event_test.go new file mode 100644 index 0000000..937dfb3 --- /dev/null +++ b/cmd/event_test.go @@ -0,0 +1,87 @@ +package cmd + +import ( + "bytes" + "fmt" + "github.com/cobbler/cobblerclient" + "github.com/spf13/cobra" + "io" + "strings" + "testing" +) + +func Test_EventStatusCmd(t *testing.T) { + // Arrange + cobra.OnInitialize(initConfig, setupLogger) + rootCmd := NewRootCmd() + rootCmd.SetArgs([]string{"--config", "../testing/.cobbler.yaml", "event", "status", "--event-id", "garbage"}) + stdout := bytes.NewBufferString("") + stderr := bytes.NewBufferString("") + rootCmd.SetOut(stdout) + rootCmd.SetErr(stderr) + + // Act + err := rootCmd.Execute() + + // Assert + if err == nil { + t.Fatal("expected error, got none") + } + if !strings.Contains(err.Error(), "no event with that id") { + t.Fatal("server didn't complain about garbage event id") + } +} + +func Test_EventListCmd(t *testing.T) { + // Arrange + cobra.OnInitialize(initConfig, setupLogger) + rootCmd := NewRootCmd() + rootCmd.SetArgs([]string{"--config", "../testing/.cobbler.yaml", "event", "list"}) + stdout := bytes.NewBufferString("") + stderr := bytes.NewBufferString("") + rootCmd.SetOut(stdout) + rootCmd.SetErr(stderr) + + // Act + err := rootCmd.Execute() + + // Assert + cobblerclient.FailOnError(t, err) + FailOnStdErr(t, stderr) + stdoutBytes, err := io.ReadAll(stdout) + if err != nil { + t.Fatal(err) + } + stdoutString := string(stdoutBytes) + if !strings.Contains(stdoutString, "Time (last transitioned)") { + fmt.Println(stdoutString) + t.Fatal("no table header with time present") + } +} + +func Test_EventLogCmd(t *testing.T) { + // Arrange + cobra.OnInitialize(initConfig, setupLogger) + rootCmd := NewRootCmd() + rootCmd.SetArgs([]string{"--config", "../testing/.cobbler.yaml", "event", "log", "--event-id", "garbage"}) + stdout := bytes.NewBufferString("") + stderr := bytes.NewBufferString("") + rootCmd.SetOut(stdout) + rootCmd.SetErr(stderr) + + // Act + err := rootCmd.Execute() + + // Assert + cobblerclient.FailOnError(t, err) + FailOnStdErr(t, stderr) + stdoutBytes, err := io.ReadAll(stdout) + if err != nil { + t.Fatal(err) + } + stdoutString := string(stdoutBytes) + if !strings.Contains(stdoutString, "?") { + fmt.Println(stdoutString) + t.Fatal("server didn't complain about garbage event id") + } +} diff --git a/cmd/file_test.go b/cmd/file_test.go new file mode 100644 index 0000000..e6f617e --- /dev/null +++ b/cmd/file_test.go @@ -0,0 +1,10 @@ +package cmd + +// TODO: file report +// TODO: file report --name=test +// TODO: file edit +// TODO: file find +// TODO: file copy +// TODO: file rename +// TODO: file add +// TODO: file remove diff --git a/cmd/hardlink_test.go b/cmd/hardlink_test.go new file mode 100644 index 0000000..cc01361 --- /dev/null +++ b/cmd/hardlink_test.go @@ -0,0 +1,38 @@ +package cmd + +import ( + "bytes" + "fmt" + "github.com/cobbler/cobblerclient" + "github.com/spf13/cobra" + "io" + "strings" + "testing" +) + +func Test_HardlinkCmd(t *testing.T) { + // Arrange + cobra.OnInitialize(initConfig, setupLogger) + rootCmd := NewRootCmd() + rootCmd.SetArgs([]string{"--config", "../testing/.cobbler.yaml", "hardlink"}) + stdout := bytes.NewBufferString("") + stderr := bytes.NewBufferString("") + rootCmd.SetOut(stdout) + rootCmd.SetErr(stderr) + + // Act + err := rootCmd.Execute() + + // Assert + cobblerclient.FailOnError(t, err) + FailOnStdErr(t, stderr) + stdoutBytes, err := io.ReadAll(stdout) + if err != nil { + t.Fatal(err) + } + stdoutString := string(stdoutBytes) + if !strings.Contains(stdoutString, "Event ID:") { + fmt.Println(stdoutString) + t.Fatal("No Event ID present") + } +} diff --git a/cmd/image_test.go b/cmd/image_test.go new file mode 100644 index 0000000..601041a --- /dev/null +++ b/cmd/image_test.go @@ -0,0 +1,10 @@ +package cmd + +// TODO: image report +// TODO: image report --name=test +// TODO: image edit +// TODO: image find +// TODO: image copy +// TODO: image rename +// TODO: image add +// TODO: image remove diff --git a/cmd/import_test.go b/cmd/import_test.go new file mode 100644 index 0000000..6b9d56f --- /dev/null +++ b/cmd/import_test.go @@ -0,0 +1,58 @@ +package cmd + +import ( + "bytes" + "fmt" + "github.com/cobbler/cobblerclient" + "github.com/spf13/cobra" + "io" + "strings" + "testing" +) + +func Test_ImportCmd(t *testing.T) { + type args struct { + command []string + } + tests := []struct { + name string + args args + want string + wantErr bool + }{ + { + name: "plain", + args: args{command: []string{"--config", "../testing/.cobbler.yaml", "import"}}, + want: "Event ID:", + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Arrange + cobra.OnInitialize(initConfig, setupLogger) + rootCmd := NewRootCmd() + rootCmd.SetArgs(tt.args.command) + stdout := bytes.NewBufferString("") + stderr := bytes.NewBufferString("") + rootCmd.SetOut(stdout) + rootCmd.SetErr(stderr) + + // Act + err := rootCmd.Execute() + + // Assert + cobblerclient.FailOnError(t, err) + FailOnStdErr(t, stderr) + stdoutBytes, err := io.ReadAll(stdout) + if err != nil { + t.Fatal(err) + } + stdoutString := string(stdoutBytes) + if !strings.Contains(stdoutString, tt.want) { + fmt.Println(stdoutString) + t.Fatal("No Event ID present") + } + }) + } +} diff --git a/cmd/list_test.go b/cmd/list_test.go new file mode 100644 index 0000000..9757b61 --- /dev/null +++ b/cmd/list_test.go @@ -0,0 +1,38 @@ +package cmd + +import ( + "bytes" + "fmt" + "github.com/cobbler/cobblerclient" + "github.com/spf13/cobra" + "io" + "strings" + "testing" +) + +func Test_ListCmd(t *testing.T) { + // Arrange + cobra.OnInitialize(initConfig, setupLogger) + rootCmd := NewRootCmd() + rootCmd.SetArgs([]string{"--config", "../testing/.cobbler.yaml", "list"}) + stdout := bytes.NewBufferString("") + stderr := bytes.NewBufferString("") + rootCmd.SetOut(stdout) + rootCmd.SetErr(stderr) + + // Act + err := rootCmd.Execute() + + // Assert + cobblerclient.FailOnError(t, err) + FailOnStdErr(t, stderr) + stdoutBytes, err := io.ReadAll(stdout) + if err != nil { + t.Fatal(err) + } + stdoutString := string(stdoutBytes) + if !(strings.Contains(stdoutString, "distros:") && strings.Contains(stdoutString, "profiles")) { + fmt.Println(stdoutString) + t.Fatal("no heading for distros and profiles present") + } +} diff --git a/cmd/menu_test.go b/cmd/menu_test.go new file mode 100644 index 0000000..f7bb6d5 --- /dev/null +++ b/cmd/menu_test.go @@ -0,0 +1,10 @@ +package cmd + +// TODO: menu report +// TODO: menu report --name=test +// TODO: menu edit +// TODO: menu find +// TODO: menu copy +// TODO: menu rename +// TODO: menu add +// TODO: menu remove diff --git a/cmd/mgmtclass_test.go b/cmd/mgmtclass_test.go new file mode 100644 index 0000000..f4743dc --- /dev/null +++ b/cmd/mgmtclass_test.go @@ -0,0 +1,10 @@ +package cmd + +// TODO: mgmtclass report +// TODO: mgmtclass report --name=test +// TODO: mgmtclass edit +// TODO: mgmtclass find +// TODO: mgmtclass copy +// TODO: mgmtclass rename +// TODO: mgmtclass add +// TODO: mgmtclass remove diff --git a/cmd/mkloaders_test.go b/cmd/mkloaders_test.go new file mode 100644 index 0000000..a8d0d3a --- /dev/null +++ b/cmd/mkloaders_test.go @@ -0,0 +1,38 @@ +package cmd + +import ( + "bytes" + "fmt" + "github.com/cobbler/cobblerclient" + "github.com/spf13/cobra" + "io" + "strings" + "testing" +) + +func Test_MkLoaders(t *testing.T) { + // Arrange + cobra.OnInitialize(initConfig, setupLogger) + rootCmd := NewRootCmd() + rootCmd.SetArgs([]string{"--config", "../testing/.cobbler.yaml", "mkloaders"}) + stdout := bytes.NewBufferString("") + stderr := bytes.NewBufferString("") + rootCmd.SetOut(stdout) + rootCmd.SetErr(stderr) + + // Act + err := rootCmd.Execute() + + // Assert + cobblerclient.FailOnError(t, err) + FailOnStdErr(t, stderr) + stdoutBytes, err := io.ReadAll(stdout) + if err != nil { + t.Fatal(err) + } + stdoutString := string(stdoutBytes) + if !strings.Contains(stdoutString, "Event ID:") { + fmt.Println(stdoutString) + t.Fatal("No Event ID present") + } +} diff --git a/cmd/package_test.go b/cmd/package_test.go new file mode 100644 index 0000000..a998bb4 --- /dev/null +++ b/cmd/package_test.go @@ -0,0 +1,10 @@ +package cmd + +// TODO: package report +// TODO: package report --name=test +// TODO: package edit +// TODO: package find +// TODO: package copy +// TODO: package rename +// TODO: package add +// TODO: package remove diff --git a/cmd/profile_test.go b/cmd/profile_test.go new file mode 100644 index 0000000..c827828 --- /dev/null +++ b/cmd/profile_test.go @@ -0,0 +1,10 @@ +package cmd + +// TODO: profile report +// TODO: profile report --name=test +// TODO: profile edit +// TODO: profile find +// TODO: profile copy +// TODO: profile rename +// TODO: profile add +// TODO: profile remove diff --git a/cmd/replicate_test.go b/cmd/replicate_test.go new file mode 100644 index 0000000..ec213e3 --- /dev/null +++ b/cmd/replicate_test.go @@ -0,0 +1,58 @@ +package cmd + +import ( + "bytes" + "fmt" + "github.com/cobbler/cobblerclient" + "github.com/spf13/cobra" + "io" + "strings" + "testing" +) + +func Test_ReplicateCmd(t *testing.T) { + type args struct { + command []string + } + tests := []struct { + name string + args args + want string + wantErr bool + }{ + { + name: "plain", + args: args{command: []string{"--config", "../testing/.cobbler.yaml", "replicate"}}, + want: "Event ID:", + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Arrange + cobra.OnInitialize(initConfig, setupLogger) + rootCmd := NewRootCmd() + rootCmd.SetArgs(tt.args.command) + stdout := bytes.NewBufferString("") + stderr := bytes.NewBufferString("") + rootCmd.SetOut(stdout) + rootCmd.SetErr(stderr) + + // Act + err := rootCmd.Execute() + + // Assert + cobblerclient.FailOnError(t, err) + FailOnStdErr(t, stderr) + stdoutBytes, err := io.ReadAll(stdout) + if err != nil { + t.Fatal(err) + } + stdoutString := string(stdoutBytes) + if !strings.Contains(stdoutString, tt.want) { + fmt.Println(stdoutString) + t.Fatal("No Event ID present") + } + }) + } +} diff --git a/cmd/repo_test.go b/cmd/repo_test.go new file mode 100644 index 0000000..11bbd97 --- /dev/null +++ b/cmd/repo_test.go @@ -0,0 +1,10 @@ +package cmd + +// TODO: repo report +// TODO: repo report --name=test +// TODO: repo edit +// TODO: repo find +// TODO: repo copy +// TODO: repo rename +// TODO: repo add +// TODO: repo remove diff --git a/cmd/report_test.go b/cmd/report_test.go new file mode 100644 index 0000000..d037730 --- /dev/null +++ b/cmd/report_test.go @@ -0,0 +1,38 @@ +package cmd + +import ( + "bytes" + "fmt" + "github.com/cobbler/cobblerclient" + "github.com/spf13/cobra" + "io" + "strings" + "testing" +) + +func Test_ReportCmd(t *testing.T) { + // Arrange + cobra.OnInitialize(initConfig, setupLogger) + rootCmd := NewRootCmd() + rootCmd.SetArgs([]string{"--config", "../testing/.cobbler.yaml", "report"}) + stdout := bytes.NewBufferString("") + stderr := bytes.NewBufferString("") + rootCmd.SetOut(stdout) + rootCmd.SetErr(stderr) + + // Act + err := rootCmd.Execute() + + // Assert + cobblerclient.FailOnError(t, err) + FailOnStdErr(t, stderr) + stdoutBytes, err := io.ReadAll(stdout) + if err != nil { + t.Fatal(err) + } + stdoutString := string(stdoutBytes) + if !(strings.Contains(stdoutString, "distros:") && strings.Contains(stdoutString, "profiles")) { + fmt.Println(stdoutString) + t.Fatal("no heading for distros and profiles present") + } +} diff --git a/cmd/reposync_test.go b/cmd/reposync_test.go new file mode 100644 index 0000000..e3af61c --- /dev/null +++ b/cmd/reposync_test.go @@ -0,0 +1,70 @@ +package cmd + +import ( + "bytes" + "fmt" + "github.com/cobbler/cobblerclient" + "github.com/spf13/cobra" + "io" + "strings" + "testing" +) + +func Test_ReposyncCmd(t *testing.T) { + type args struct { + command []string + } + tests := []struct { + name string + args args + want string + wantErr bool + }{ + { + name: "plain", + args: args{command: []string{"--config", "../testing/.cobbler.yaml", "reposync"}}, + want: "Event ID:", + wantErr: false, + }, + { + name: "tries", + args: args{command: []string{"--config", "../testing/.cobbler.yaml", "reposync", "--tries", "3"}}, + want: "Event ID:", + wantErr: false, + }, + { + name: "nofail", + args: args{command: []string{"--config", "../testing/.cobbler.yaml", "reposync", "--no-fail"}}, + want: "Event ID:", + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Arrange + cobra.OnInitialize(initConfig, setupLogger) + rootCmd := NewRootCmd() + rootCmd.SetArgs(tt.args.command) + stdout := bytes.NewBufferString("") + stderr := bytes.NewBufferString("") + rootCmd.SetOut(stdout) + rootCmd.SetErr(stderr) + + // Act + err := rootCmd.Execute() + + // Assert + cobblerclient.FailOnError(t, err) + FailOnStdErr(t, stderr) + stdoutBytes, err := io.ReadAll(stdout) + if err != nil { + t.Fatal(err) + } + stdoutString := string(stdoutBytes) + if !strings.Contains(stdoutString, tt.want) { + fmt.Println(stdoutString) + t.Fatal("No Event ID present") + } + }) + } +} diff --git a/cmd/setting_test.go b/cmd/setting_test.go new file mode 100644 index 0000000..d8caf6e --- /dev/null +++ b/cmd/setting_test.go @@ -0,0 +1,60 @@ +package cmd + +import ( + "bytes" + "fmt" + "github.com/cobbler/cobblerclient" + "github.com/spf13/cobra" + "io" + "strings" + "testing" +) + +func Test_SettingEditCmd(t *testing.T) { + // Arrange + cobra.OnInitialize(initConfig, setupLogger) + rootCmd := NewRootCmd() + rootCmd.SetArgs([]string{"--config", "../testing/.cobbler.yaml", "setting", "edit"}) + stdout := bytes.NewBufferString("") + stderr := bytes.NewBufferString("") + rootCmd.SetOut(stdout) + rootCmd.SetErr(stderr) + + // Act + err := rootCmd.Execute() + + // Assert + if err == nil { + t.Fatal("expected error, got none") + } + if err.Error() != "dynamic settings are turned off server-side" { + t.Fatalf("expected dynamic settings are to be turned off server-side, got %s", err.Error()) + } +} + +func Test_SettingReportCmd(t *testing.T) { + // Arrange + cobra.OnInitialize(initConfig, setupLogger) + rootCmd := NewRootCmd() + rootCmd.SetArgs([]string{"--config", "../testing/.cobbler.yaml", "setting", "report"}) + stdout := bytes.NewBufferString("") + stderr := bytes.NewBufferString("") + rootCmd.SetOut(stdout) + rootCmd.SetErr(stderr) + + // Act + err := rootCmd.Execute() + + // Assert + cobblerclient.FailOnError(t, err) + FailOnStdErr(t, stderr) + stdoutBytes, err := io.ReadAll(stdout) + if err != nil { + t.Fatal(err) + } + stdoutString := string(stdoutBytes) + if !strings.Contains(stdoutString, "scm_track_enabled") { + fmt.Println(stdoutString) + t.Fatal("Expected setting couldn't be found") + } +} diff --git a/cmd/signature_test.go b/cmd/signature_test.go new file mode 100644 index 0000000..863cb4d --- /dev/null +++ b/cmd/signature_test.go @@ -0,0 +1,92 @@ +package cmd + +import ( + "bytes" + "fmt" + "github.com/cobbler/cobblerclient" + "github.com/spf13/cobra" + "io" + "strings" + "testing" +) + +func Test_SignatureReloadCmd(t *testing.T) { + // Arrange + cobra.OnInitialize(initConfig, setupLogger) + rootCmd := NewRootCmd() + rootCmd.SetArgs([]string{"--config", "../testing/.cobbler.yaml", "signature", "reload"}) + stdout := bytes.NewBufferString("") + stderr := bytes.NewBufferString("") + rootCmd.SetOut(stdout) + rootCmd.SetErr(stderr) + + // Act + err := rootCmd.Execute() + + // Assert + cobblerclient.FailOnError(t, err) + FailOnStdErr(t, stderr) + stdoutBytes, err := io.ReadAll(stdout) + if err != nil { + t.Fatal(err) + } + stdoutString := string(stdoutBytes) + if !strings.Contains(stdoutString, "This functionality cannot be used in the new CLI") { + fmt.Println(stdoutString) + t.Fatal("No missing feature message present") + } +} + +func Test_SignatureReportCmd(t *testing.T) { + // Arrange + cobra.OnInitialize(initConfig, setupLogger) + rootCmd := NewRootCmd() + rootCmd.SetArgs([]string{"--config", "../testing/.cobbler.yaml", "signature", "report"}) + stdout := bytes.NewBufferString("") + stderr := bytes.NewBufferString("") + rootCmd.SetOut(stdout) + rootCmd.SetErr(stderr) + + // Act + err := rootCmd.Execute() + + // Assert + cobblerclient.FailOnError(t, err) + FailOnStdErr(t, stderr) + stdoutBytes, err := io.ReadAll(stdout) + if err != nil { + t.Fatal(err) + } + stdoutString := string(stdoutBytes) + if !strings.Contains(stdoutString, "Currently loaded signatures") { + fmt.Println(stdoutString) + t.Fatal("No report header present") + } +} + +func Test_SignatureUpdateCmd(t *testing.T) { + // Arrange + cobra.OnInitialize(initConfig, setupLogger) + rootCmd := NewRootCmd() + rootCmd.SetArgs([]string{"--config", "../testing/.cobbler.yaml", "signature", "update"}) + stdout := bytes.NewBufferString("") + stderr := bytes.NewBufferString("") + rootCmd.SetOut(stdout) + rootCmd.SetErr(stderr) + + // Act + err := rootCmd.Execute() + + // Assert + cobblerclient.FailOnError(t, err) + FailOnStdErr(t, stderr) + stdoutBytes, err := io.ReadAll(stdout) + if err != nil { + t.Fatal(err) + } + stdoutString := string(stdoutBytes) + if !strings.Contains(stdoutString, "Event ID:") { + fmt.Println(stdoutString) + t.Fatal("No Event ID present") + } +} diff --git a/cmd/sync_test.go b/cmd/sync_test.go new file mode 100644 index 0000000..708132c --- /dev/null +++ b/cmd/sync_test.go @@ -0,0 +1,82 @@ +package cmd + +import ( + "bytes" + "fmt" + "github.com/cobbler/cobblerclient" + "github.com/spf13/cobra" + "io" + "strings" + "testing" +) + +func Test_SyncCmd(t *testing.T) { + type args struct { + command []string + } + tests := []struct { + name string + args args + want string + wantErr bool + }{ + { + name: "plain", + args: args{command: []string{"--config", "../testing/.cobbler.yaml", "sync"}}, + want: "Event ID:", + wantErr: false, + }, + { + name: "dns", + args: args{command: []string{"--config", "../testing/.cobbler.yaml", "sync", "--dns"}}, + want: "Event ID:", + wantErr: false, + }, + { + name: "dhcp", + args: args{command: []string{"--config", "../testing/.cobbler.yaml", "sync", "--dhcp"}}, + want: "Event ID:", + wantErr: false, + }, + { + name: "dhcpdns", + args: args{command: []string{"--config", "../testing/.cobbler.yaml", "sync", "--dns", "--dhcp"}}, + want: "Event ID:", + wantErr: false, + }, + { + name: "systems", + args: args{command: []string{"--config", "../testing/.cobbler.yaml", "sync", "--systems", "a.b.c,a.d.c"}}, + want: "Event ID:", + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Arrange + cobra.OnInitialize(initConfig, setupLogger) + rootCmd := NewRootCmd() + rootCmd.SetArgs(tt.args.command) + stdout := bytes.NewBufferString("") + stderr := bytes.NewBufferString("") + rootCmd.SetOut(stdout) + rootCmd.SetErr(stderr) + + // Act + err := rootCmd.Execute() + + // Assert + cobblerclient.FailOnError(t, err) + FailOnStdErr(t, stderr) + stdoutBytes, err := io.ReadAll(stdout) + if err != nil { + t.Fatal(err) + } + stdoutString := string(stdoutBytes) + if !strings.Contains(stdoutString, tt.want) { + fmt.Println(stdoutString) + t.Fatal("No Event ID present") + } + }) + } +} diff --git a/cmd/system_test.go b/cmd/system_test.go new file mode 100644 index 0000000..e564350 --- /dev/null +++ b/cmd/system_test.go @@ -0,0 +1,10 @@ +package cmd + +// TODO: system report +// TODO: system report --name=test +// TODO: system edit +// TODO: system find +// TODO: system copy +// TODO: system rename +// TODO: system add +// TODO: system remove diff --git a/cmd/testing.go b/cmd/testing.go new file mode 100644 index 0000000..fad2f98 --- /dev/null +++ b/cmd/testing.go @@ -0,0 +1,12 @@ +package cmd + +import ( + "bytes" + "testing" +) + +func FailOnStdErr(t *testing.T, buffer *bytes.Buffer) { + if buffer.Available() > 0 { + t.Fatal("stderr wasn't empty!") + } +} diff --git a/cmd/utils_test.go b/cmd/utils_test.go index c743d63..a82235f 100644 --- a/cmd/utils_test.go +++ b/cmd/utils_test.go @@ -1,7 +1,6 @@ package cmd import ( - "fmt" "reflect" "testing" "time" @@ -26,7 +25,6 @@ func Test_covertFloatToTime(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := covertFloatToUtcTime(tt.args.t) - fmt.Println(got) if (err != nil) != tt.wantErr { t.Errorf("covertFloatToTime() error = %v, wantErr %v", err, tt.wantErr) return diff --git a/cmd/validateAutoinstalls_test.go b/cmd/validateAutoinstalls_test.go new file mode 100644 index 0000000..dc7c556 --- /dev/null +++ b/cmd/validateAutoinstalls_test.go @@ -0,0 +1,38 @@ +package cmd + +import ( + "bytes" + "fmt" + "github.com/cobbler/cobblerclient" + "github.com/spf13/cobra" + "io" + "strings" + "testing" +) + +func Test_ValidateAutoinstallsCmd(t *testing.T) { + // Arrange + cobra.OnInitialize(initConfig, setupLogger) + rootCmd := NewRootCmd() + rootCmd.SetArgs([]string{"--config", "../testing/.cobbler.yaml", "validate-autoinstalls"}) + stdout := bytes.NewBufferString("") + stderr := bytes.NewBufferString("") + rootCmd.SetOut(stdout) + rootCmd.SetErr(stderr) + + // Act + err := rootCmd.Execute() + + // Assert + cobblerclient.FailOnError(t, err) + FailOnStdErr(t, stderr) + stdoutBytes, err := io.ReadAll(stdout) + if err != nil { + t.Fatal(err) + } + stdoutString := string(stdoutBytes) + if !strings.Contains(stdoutString, "Event ID:") { + fmt.Println(stdoutString) + t.Fatal("No Event ID present") + } +} diff --git a/cmd/version_test.go b/cmd/version_test.go new file mode 100644 index 0000000..cedcb22 --- /dev/null +++ b/cmd/version_test.go @@ -0,0 +1,38 @@ +package cmd + +import ( + "bytes" + "fmt" + "github.com/cobbler/cobblerclient" + "github.com/spf13/cobra" + "io" + "strings" + "testing" +) + +func Test_VersionCommand(t *testing.T) { + // Arrange + cobra.OnInitialize(initConfig, setupLogger) + rootCmd := NewRootCmd() + rootCmd.SetArgs([]string{"--config", "../testing/.cobbler.yaml", "version"}) + stdout := bytes.NewBufferString("") + stderr := bytes.NewBufferString("") + rootCmd.SetOut(stdout) + rootCmd.SetErr(stderr) + + // Act + err := rootCmd.Execute() + + // Assert + cobblerclient.FailOnError(t, err) + FailOnStdErr(t, stderr) + stdoutBytes, err := io.ReadAll(stdout) + if err != nil { + t.Fatal(err) + } + stdoutString := string(stdoutBytes) + if !strings.Contains(stdoutString, "source: ?, ?") { + fmt.Println(stdoutString) + t.Fatal("CLI version not part of the return string") + } +} diff --git a/testing/.cobbler.yaml b/testing/.cobbler.yaml new file mode 100644 index 0000000..6531712 --- /dev/null +++ b/testing/.cobbler.yaml @@ -0,0 +1,3 @@ +server_url: "http://127.0.0.1:8081/cobbler_api" +server_username: "cobbler" +server_password: "cobbler" diff --git a/testing/compose.yml b/testing/compose.yml new file mode 100644 index 0000000..f02d3f3 --- /dev/null +++ b/testing/compose.yml @@ -0,0 +1,17 @@ +services: + cobbler: + image: cobbler-dev + container_name: cobbler-dev + privileged: true # Required for Cobbler 3.3.2 and newer + volumes: + - ./cobbler_source:/code + - ../extracted_iso_image:/extracted_iso_image + ports: + - 8081:80 + # We chmod the code, otherwise some files are read-only and cannot be cleaned up: + command: bash -c " + /code/docker/develop/scripts/setup-supervisor.sh && + chmod -R o+w /code && + cobbler import --name Ubuntu-20.04 --breed ubuntu --path /extracted_iso_image/ && + tail -F /dev/null + " diff --git a/testing/start.sh b/testing/start.sh new file mode 100755 index 0000000..1d75463 --- /dev/null +++ b/testing/start.sh @@ -0,0 +1,70 @@ +#! /bin/bash + +# Requires xorriso (sudo apt-get install -y xorriso, sudo yum install xorriso -y, or sudo zypper install -y xorriso) +if [ -z "$1" ] + then + echo "No cobbler server url supplied" +fi + +cobbler_commit=df356046f3cf27be62a61001b982d5983800cfd9 # 3.3.6 as of 2024-10-09 +cobbler_branch=release33 +iso_url=https://cdimage.ubuntu.com/ubuntu-legacy-server/releases/20.04/release/ubuntu-20.04.1-legacy-server-amd64.iso +iso_os=ubuntu +valid_iso_checksum=00a9d46306fbe9beb3581853a289490bc231c51f +iso_filename=$(echo ${iso_url##*/}) +valid_extracted_iso_checksum=dd0b3148e1f071fb86aee4b0395fd63b +valid_git_checksum=6c9511b26946dd3f1f072b9f40eaeccf # master as of 4/2/2022 + +[ -d "./testing/cobbler_source" ] && git_checksum=$(find ./testing/cobbler_source/ -type f -exec md5sum {} \; | sort -k 2 | md5sum | awk '{print $1}') +if [ -d "./testing/cobbler_source" ] && [ $git_checksum == $valid_git_checksum ]; then + echo "Cobbler code already cloned and the correct version is checked out" +else + rm -rf ./testing/cobbler_source + git clone --shallow-since="2021-09-01" https://github.com/cobbler/cobbler.git -b $cobbler_branch testing/cobbler_source + cd ./testing/cobbler_source + printf "Changing to version of Cobbler being tested.\n\n" + git checkout $cobbler_commit > /dev/null 2>&1 + rm -rf .git # remove .git dir so the checksum is consistent + cd - +fi + +echo $(pwd) +if [ -f "$iso_filename" ] && [ $(sha1sum $iso_filename | awk '{print $1}') == "$valid_iso_checksum" ]; then + echo "ISO already downloaded" +else + rm $iso_filename + wget $iso_url +fi + +extracted_iso_checksum=$(find extracted_iso_image -type f -exec md5sum {} \; | sort -k 2 | md5sum | awk '{print $1}') +if [ -d "extracted_iso_image" ] && [ $extracted_iso_checksum == $valid_extracted_iso_checksum ]; then + echo "ISO already extracted" +else + xorriso -osirrox on -indev $iso_filename -extract / extracted_iso_image +fi + +docker build -f ./testing/cobbler_source/docker/develop/develop.dockerfile -t cobbler-dev . +docker compose -f testing/compose.yml up -d + +SERVER_URL=$1 +printf "### Waiting for Cobbler to become available on ${SERVER_URL} \n\n" + +attempt_counter=0 +max_attempts=48 + +until $(curl --connect-timeout 1 --output /dev/null --silent ${SERVER_URL}); do + if [ ${attempt_counter} -eq ${max_attempts} ];then + echo "Max attempts reached" + # Debug logs + docker compose -f ./testing/compose.yml logs + exit 1 + fi + + attempt_counter=$(($attempt_counter+1)) + sleep 5 +done + +# Sleep 10 seconds to let the "cobbler import" succeed +sleep 10 + +docker compose -f testing/compose.yml logs \ No newline at end of file