mirror of
https://github.com/go-sigma/sigma.git
synced 2026-10-28 18:04:33 +08:00
Replace all usages of standard encoding/json with encoding/json/v2 and jsontext where applicable, update decoding logic to use json.UnmarshalRead and json.MarshalWrite instead of manual encoder/decoder instances. Verification: go test -race ./pkg/... ./cmd/...
272 lines
8.8 KiB
Go
272 lines
8.8 KiB
Go
// Copyright 2026 sigma
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json/v2"
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"net/url"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/spf13/cobra"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/go-sigma/sigma/pkg/api/enums"
|
|
)
|
|
|
|
func TestNewRootCmd(t *testing.T) {
|
|
t.Setenv("SIGMA_SERVER", "http://example.test")
|
|
|
|
command := newRootCmd()
|
|
|
|
require.Equal(t, "sigma-cli", command.Use)
|
|
require.NotNil(t, findCommand(t, command, "login"))
|
|
require.NotNil(t, findCommand(t, command, "namespace"))
|
|
require.NotNil(t, findCommand(t, command, "repo"))
|
|
require.NotNil(t, findCommand(t, command, "tag"))
|
|
require.NotNil(t, findCommand(t, command, "artifact"))
|
|
require.NotNil(t, findCommand(t, command, "member"))
|
|
require.NotNil(t, findCommand(t, command, "user"))
|
|
}
|
|
|
|
func TestOptionsInitialize(t *testing.T) {
|
|
credentialsFile := filepath.Join(t.TempDir(), "credentials.json")
|
|
require.NoError(t, saveCredentials(credentialsFile, credentials{
|
|
Server: "http://saved.example.test/",
|
|
Username: "sigma",
|
|
Password: "secret",
|
|
}))
|
|
opts := &options{
|
|
server: "http://flag.example.test/",
|
|
credentialsFile: credentialsFile,
|
|
timeout: time.Second,
|
|
}
|
|
command := &cobra.Command{Use: "test"}
|
|
command.PersistentFlags().String("server", opts.server, "")
|
|
|
|
require.NoError(t, opts.initialize(context.Background(), command))
|
|
require.Equal(t, "http://saved.example.test", opts.client.baseURL)
|
|
require.Equal(t, "sigma", opts.client.username)
|
|
require.Equal(t, "secret", opts.client.password)
|
|
|
|
require.NoError(t, command.PersistentFlags().Set("server", "http://flag.example.test/"))
|
|
require.NoError(t, opts.initialize(context.Background(), command))
|
|
require.Equal(t, "http://flag.example.test", opts.client.baseURL)
|
|
}
|
|
|
|
func TestOptionsInitializeRequiresCredentials(t *testing.T) {
|
|
opts := &options{
|
|
server: "http://example.test",
|
|
credentialsFile: filepath.Join(t.TempDir(), "missing.json"),
|
|
timeout: time.Second,
|
|
}
|
|
command := &cobra.Command{Use: "test"}
|
|
command.PersistentFlags().String("server", opts.server, "")
|
|
|
|
err := opts.initialize(context.Background(), command)
|
|
|
|
require.ErrorContains(t, err, "basic auth credentials are required")
|
|
}
|
|
|
|
func TestNewLoginCmd(t *testing.T) {
|
|
credentialsFile := filepath.Join(t.TempDir(), "credentials.json")
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
require.Equal(t, http.MethodPost, r.Method)
|
|
require.Equal(t, "/api/v1/users/login", r.URL.Path)
|
|
username, password, ok := r.BasicAuth()
|
|
require.True(t, ok)
|
|
require.Equal(t, "sigma", username)
|
|
require.Equal(t, "secret", password)
|
|
w.WriteHeader(http.StatusOK)
|
|
}))
|
|
t.Cleanup(server.Close)
|
|
|
|
opts := &options{
|
|
server: server.URL + "/",
|
|
credentialsFile: credentialsFile,
|
|
timeout: time.Second,
|
|
}
|
|
command := newLoginCmd(opts)
|
|
output := executeCommand(t, command, "-u", "sigma", "-p", "secret")
|
|
|
|
require.Contains(t, output, `"status": "logged_in"`)
|
|
cred, err := loadCredentials(credentialsFile)
|
|
require.NoError(t, err)
|
|
require.Equal(t, server.URL, cred.Server)
|
|
require.Equal(t, "sigma", cred.Username)
|
|
require.Equal(t, "secret", cred.Password)
|
|
}
|
|
|
|
func TestClientDo(t *testing.T) {
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
require.Equal(t, http.MethodPost, r.Method)
|
|
require.Equal(t, "/api/v1/test", r.URL.Path)
|
|
require.Equal(t, "value", r.URL.Query().Get("name"))
|
|
username, password, ok := r.BasicAuth()
|
|
require.True(t, ok)
|
|
require.Equal(t, "override", username)
|
|
require.Equal(t, "secret", password)
|
|
var body map[string]string
|
|
require.NoError(t, json.UnmarshalRead(r.Body, &body))
|
|
require.Equal(t, "payload", body["name"])
|
|
w.Header().Set("Content-Type", "application/json")
|
|
_, err := w.Write([]byte(`{"status":"ok"}`))
|
|
require.NoError(t, err)
|
|
}))
|
|
t.Cleanup(server.Close)
|
|
|
|
cli := newClient(server.URL, "default", "default-secret", time.Second, false)
|
|
var out map[string]string
|
|
query := url.Values{"name": []string{"value"}}
|
|
err := cli.do(t.Context(), http.MethodPost, "/api/v1/test", query, map[string]string{"name": "payload"}, &out, basicAuth{
|
|
username: "override",
|
|
password: "secret",
|
|
})
|
|
|
|
require.NoError(t, err)
|
|
require.Equal(t, "ok", out["status"])
|
|
}
|
|
|
|
func TestClientDoReturnsFormattedError(t *testing.T) {
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
_, err := w.Write([]byte(`{"message":"bad request"}`))
|
|
require.NoError(t, err)
|
|
}))
|
|
t.Cleanup(server.Close)
|
|
|
|
err := newClient(server.URL, "", "", time.Second, false).
|
|
do(t.Context(), http.MethodGet, "/api/v1/fail", nil, nil, nil, basicAuth{})
|
|
|
|
require.ErrorContains(t, err, "GET /api/v1/fail")
|
|
require.ErrorContains(t, err, `"message": "bad request"`)
|
|
}
|
|
|
|
func TestCredentialsFileLifecycle(t *testing.T) {
|
|
missing := filepath.Join(t.TempDir(), "missing.json")
|
|
cred, err := loadCredentials(missing)
|
|
require.NoError(t, err)
|
|
require.Empty(t, cred)
|
|
|
|
path := filepath.Join(t.TempDir(), "nested", "credentials.json")
|
|
expected := credentials{
|
|
Server: "http://example.test",
|
|
Username: "sigma",
|
|
Password: "secret",
|
|
}
|
|
require.NoError(t, saveCredentials(path, expected))
|
|
|
|
info, err := os.Stat(path)
|
|
require.NoError(t, err)
|
|
require.Equal(t, os.FileMode(0o600), info.Mode().Perm())
|
|
|
|
actual, err := loadCredentials(path)
|
|
require.NoError(t, err)
|
|
require.Equal(t, expected, actual)
|
|
|
|
invalid := filepath.Join(t.TempDir(), "invalid.json")
|
|
require.NoError(t, os.WriteFile(invalid, []byte("{"), 0o600))
|
|
_, err = loadCredentials(invalid)
|
|
require.ErrorContains(t, err, "decode credentials file")
|
|
}
|
|
|
|
func TestEnvAndCredentialsFileDefaults(t *testing.T) {
|
|
t.Setenv("SIGMA_TEST_ENV", " configured ")
|
|
require.Equal(t, "configured", envOrDefault("SIGMA_TEST_ENV", "fallback"))
|
|
t.Setenv("SIGMA_TEST_ENV", " ")
|
|
require.Equal(t, "fallback", envOrDefault("SIGMA_TEST_ENV", "fallback"))
|
|
|
|
t.Setenv("SIGMA_CREDENTIALS_FILE", "/tmp/sigma-creds.json")
|
|
require.Equal(t, "/tmp/sigma-creds.json", defaultCredentialsFile())
|
|
}
|
|
|
|
func TestCommonListQuery(t *testing.T) {
|
|
query := commonListQuery("sigma", 2, 50, "created_at", "desc")
|
|
|
|
require.Equal(t, "sigma", query.Get("name"))
|
|
require.Equal(t, "2", query.Get("page"))
|
|
require.Equal(t, "50", query.Get("limit"))
|
|
require.Equal(t, "created_at", query.Get("sort"))
|
|
require.Equal(t, "desc", query.Get("method"))
|
|
|
|
require.Empty(t, commonListQuery("", 0, 0, "", ""))
|
|
}
|
|
|
|
func TestOptionalFlagValues(t *testing.T) {
|
|
command := &cobra.Command{Use: "test"}
|
|
addNamespaceWriteFlags(command)
|
|
require.NoError(t, command.Flags().Set("description", "desc"))
|
|
require.NoError(t, command.Flags().Set("size-limit", "1024"))
|
|
require.NoError(t, command.Flags().Set("visibility", enums.VisibilityPrivate.String()))
|
|
|
|
require.Equal(t, "desc", *optionalString(command, "description"))
|
|
require.Equal(t, int64(1024), *optionalInt64(command, "size-limit"))
|
|
visibility, err := optionalVisibility(command)
|
|
require.NoError(t, err)
|
|
require.Equal(t, enums.VisibilityPrivate, *visibility)
|
|
|
|
require.Nil(t, optionalString(command, "overview"))
|
|
|
|
invalid := &cobra.Command{Use: "invalid"}
|
|
invalid.Flags().String("visibility", "", "")
|
|
require.NoError(t, invalid.Flags().Set("visibility", "bad"))
|
|
_, err = optionalVisibility(invalid)
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestFormatErrorBodyAndEndpoint(t *testing.T) {
|
|
require.Equal(t, "{\n \"message\": \"bad\"\n}", formatErrorBody([]byte(`{"message":"bad"}`)))
|
|
require.Equal(t, "plain error", formatErrorBody([]byte(" plain error ")))
|
|
require.Equal(t, "/api/v1/namespaces/ns", endpoint("/api/v1/namespaces/%s", "ns"))
|
|
}
|
|
|
|
func findCommand(t *testing.T, root *cobra.Command, name string) *cobra.Command {
|
|
t.Helper()
|
|
command, _, err := root.Find([]string{name})
|
|
require.NoError(t, err)
|
|
return command
|
|
}
|
|
|
|
func executeCommand(t *testing.T, command *cobra.Command, args ...string) string {
|
|
t.Helper()
|
|
|
|
oldStdout := os.Stdout
|
|
readFile, writeFile, err := os.Pipe()
|
|
require.NoError(t, err)
|
|
os.Stdout = writeFile
|
|
|
|
command.SetArgs(args)
|
|
command.SetOut(writeFile)
|
|
command.SetErr(writeFile)
|
|
err = command.Execute()
|
|
require.NoError(t, err)
|
|
require.NoError(t, writeFile.Close())
|
|
os.Stdout = oldStdout
|
|
t.Cleanup(func() {
|
|
os.Stdout = oldStdout
|
|
})
|
|
|
|
data, err := io.ReadAll(readFile)
|
|
require.NoError(t, err)
|
|
require.NoError(t, readFile.Close())
|
|
return string(data)
|
|
}
|