1
0
mirror of https://github.com/juanfont/headscale.git synced 2026-02-07 20:04:00 +01:00
juanfont.headscale/cmd/headscale/cli/generate_test.go
2025-07-14 07:48:32 +00:00

230 lines
6.5 KiB
Go

package cli
import (
"bytes"
"encoding/json"
"strings"
"testing"
"github.com/spf13/cobra"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gopkg.in/yaml.v3"
)
func TestGenerateCommand(t *testing.T) {
// Test that the generate command exists and shows help
cmd := &cobra.Command{
Use: "headscale",
Short: "headscale - a Tailscale control server",
}
cmd.AddCommand(generateCmd)
out := new(bytes.Buffer)
cmd.SetOut(out)
cmd.SetErr(out)
cmd.SetArgs([]string{"generate", "--help"})
err := cmd.Execute()
require.NoError(t, err)
outStr := out.String()
assert.Contains(t, outStr, "Generate commands")
assert.Contains(t, outStr, "private-key")
assert.Contains(t, outStr, "Aliases:")
assert.Contains(t, outStr, "gen")
}
func TestGenerateCommandAlias(t *testing.T) {
// Test that the "gen" alias works
cmd := &cobra.Command{
Use: "headscale",
Short: "headscale - a Tailscale control server",
}
cmd.AddCommand(generateCmd)
out := new(bytes.Buffer)
cmd.SetOut(out)
cmd.SetErr(out)
cmd.SetArgs([]string{"gen", "--help"})
err := cmd.Execute()
require.NoError(t, err)
outStr := out.String()
assert.Contains(t, outStr, "Generate commands")
}
func TestGeneratePrivateKeyCommand(t *testing.T) {
tests := []struct {
name string
args []string
expectJSON bool
expectYAML bool
}{
{
name: "default output",
args: []string{"generate", "private-key"},
expectJSON: false,
expectYAML: false,
},
{
name: "json output",
args: []string{"generate", "private-key", "--output", "json"},
expectJSON: true,
expectYAML: false,
},
{
name: "yaml output",
args: []string{"generate", "private-key", "--output", "yaml"},
expectJSON: false,
expectYAML: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Note: This command calls SuccessOutput which exits the process
// We can't test the actual execution easily without mocking
// Instead, we test the command structure and that it exists
cmd := &cobra.Command{
Use: "headscale",
Short: "headscale - a Tailscale control server",
}
cmd.AddCommand(generateCmd)
cmd.PersistentFlags().StringP("output", "o", "", "Output format")
// Test that the command exists and can be found
privateKeyCmd, _, err := cmd.Find([]string{"generate", "private-key"})
require.NoError(t, err)
assert.Equal(t, "private-key", privateKeyCmd.Name())
assert.Equal(t, "Generate a private key for the headscale server", privateKeyCmd.Short)
})
}
}
func TestGeneratePrivateKeyHelp(t *testing.T) {
cmd := &cobra.Command{
Use: "headscale",
Short: "headscale - a Tailscale control server",
}
cmd.AddCommand(generateCmd)
out := new(bytes.Buffer)
cmd.SetOut(out)
cmd.SetErr(out)
cmd.SetArgs([]string{"generate", "private-key", "--help"})
err := cmd.Execute()
require.NoError(t, err)
outStr := out.String()
assert.Contains(t, outStr, "Generate a private key for the headscale server")
assert.Contains(t, outStr, "Usage:")
}
// Test the key generation logic in isolation (without SuccessOutput/ErrorOutput)
func TestPrivateKeyGeneration(t *testing.T) {
// We can't easily test the full command because it calls SuccessOutput which exits
// But we can test that the key generation produces valid output format
// This is testing the core logic that would be in the command
// In a real refactor, we'd extract this to a testable function
// For now, we can test that the command structure is correct
assert.NotNil(t, generatePrivateKeyCmd)
assert.Equal(t, "private-key", generatePrivateKeyCmd.Use)
assert.Equal(t, "Generate a private key for the headscale server", generatePrivateKeyCmd.Short)
assert.NotNil(t, generatePrivateKeyCmd.Run)
}
func TestGenerateCommandStructure(t *testing.T) {
// Test the command hierarchy
assert.Equal(t, "generate", generateCmd.Use)
assert.Equal(t, "Generate commands", generateCmd.Short)
assert.Contains(t, generateCmd.Aliases, "gen")
// Test that private-key is a subcommand
found := false
for _, subcmd := range generateCmd.Commands() {
if subcmd.Name() == "private-key" {
found = true
break
}
}
assert.True(t, found, "private-key should be a subcommand of generate")
}
// Helper function to test output formats (would be used if we refactored the command)
func validatePrivateKeyOutput(t *testing.T, output string, format string) {
switch format {
case "json":
var result map[string]interface{}
err := json.Unmarshal([]byte(output), &result)
require.NoError(t, err, "Output should be valid JSON")
privateKey, exists := result["private_key"]
require.True(t, exists, "JSON should contain private_key field")
keyStr, ok := privateKey.(string)
require.True(t, ok, "private_key should be a string")
require.NotEmpty(t, keyStr, "private_key should not be empty")
// Basic validation that it looks like a machine key
assert.True(t, strings.HasPrefix(keyStr, "mkey:"), "Machine key should start with mkey:")
case "yaml":
var result map[string]interface{}
err := yaml.Unmarshal([]byte(output), &result)
require.NoError(t, err, "Output should be valid YAML")
privateKey, exists := result["private_key"]
require.True(t, exists, "YAML should contain private_key field")
keyStr, ok := privateKey.(string)
require.True(t, ok, "private_key should be a string")
require.NotEmpty(t, keyStr, "private_key should not be empty")
assert.True(t, strings.HasPrefix(keyStr, "mkey:"), "Machine key should start with mkey:")
default:
// Default format should just be the key itself
assert.True(t, strings.HasPrefix(output, "mkey:"), "Default output should be the machine key")
assert.NotContains(t, output, "{", "Default output should not contain JSON")
assert.NotContains(t, output, "private_key:", "Default output should not contain YAML structure")
}
}
func TestPrivateKeyOutputFormats(t *testing.T) {
// Test cases for different output formats
// These test the validation logic we would use after refactoring
tests := []struct {
format string
sample string
}{
{
format: "json",
sample: `{"private_key": "mkey:abcd1234567890abcd1234567890abcd1234567890abcd1234567890abcd1234"}`,
},
{
format: "yaml",
sample: "private_key: mkey:abcd1234567890abcd1234567890abcd1234567890abcd1234567890abcd1234\n",
},
{
format: "",
sample: "mkey:abcd1234567890abcd1234567890abcd1234567890abcd1234567890abcd1234",
},
}
for _, tt := range tests {
t.Run("format_"+tt.format, func(t *testing.T) {
validatePrivateKeyOutput(t, tt.sample, tt.format)
})
}
}