diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..30c4cae --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,23 @@ +name: Tinyauth CI +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + test: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: "^1.23.2" + + - name: Run tests + run: go test -v ./... diff --git a/internal/utils/utils.go b/internal/utils/utils.go index 29344d5..fd88240 100644 --- a/internal/utils/utils.go +++ b/internal/utils/utils.go @@ -50,7 +50,7 @@ func ParseUsers(users string) (types.Users, error) { return usersParsed, nil } -// Root url parses parses a hostname and returns the root domain (e.g. sub1.sub2.domain.com -> domain.com) +// Root url parses parses a hostname and returns the root domain (e.g. sub1.sub2.domain.com -> sub2.domain.com) func GetRootURL(urlSrc string) (string, error) { // Make sure the url is valid urlParsed, parseErr := url.Parse(urlSrc) @@ -176,11 +176,6 @@ func GetUsers(conf string, file string) (types.Users, error) { return ParseUsers(users) } -// Check if any of the OAuth providers are configured based on the client id and secret -func OAuthConfigured(config types.Config) bool { - return (config.GithubClientId != "" && config.GithubClientSecret != "") || (config.GoogleClientId != "" && config.GoogleClientSecret != "") || (config.GenericClientId != "" && config.GenericClientSecret != "") || (config.TailscaleClientId != "" && config.TailscaleClientSecret != "") -} - // Parse the docker labels to the tinyauth labels struct func GetTinyauthLabels(labels map[string]string) types.TinyauthLabels { // Create a new tinyauth labels struct @@ -207,3 +202,8 @@ func GetTinyauthLabels(labels map[string]string) types.TinyauthLabels { // Return the tinyauth labels return tinyauthLabels } + +// Check if any of the OAuth providers are configured based on the client id and secret +func OAuthConfigured(config types.Config) bool { + return (config.GithubClientId != "" && config.GithubClientSecret != "") || (config.GoogleClientId != "" && config.GoogleClientSecret != "") || (config.GenericClientId != "" && config.GenericClientSecret != "") || (config.TailscaleClientId != "" && config.TailscaleClientSecret != "") +} diff --git a/internal/utils/utils_test.go b/internal/utils/utils_test.go new file mode 100644 index 0000000..a2dcabc --- /dev/null +++ b/internal/utils/utils_test.go @@ -0,0 +1,315 @@ +package utils_test + +import ( + "os" + "reflect" + "testing" + "tinyauth/internal/types" + "tinyauth/internal/utils" +) + +// Test the parse users function +func TestParseUsers(t *testing.T) { + t.Log("Testing parse users with a valid string") + + // Test the parse users function with a valid string + users := "user1:pass1,user2:pass2" + expected := types.Users{ + { + Username: "user1", + Password: "pass1", + }, + { + Username: "user2", + Password: "pass2", + }, + } + + result, err := utils.ParseUsers(users) + + // Check if there was an error + if err != nil { + t.Fatalf("Error parsing users: %v", err) + } + + // Check if the result is equal to the expected + if !reflect.DeepEqual(expected, result) { + t.Fatalf("Expected %v, got %v", expected, result) + } + + t.Log("Testing parse users with an invalid string") + + // Test the parse users function with an invalid string + users = "user1:pass1,user2" + + _, err = utils.ParseUsers(users) + + // There should be an error + if err == nil { + t.Fatalf("Expected error parsing users") + } +} + +// Test the get root url function +func TestGetRootURL(t *testing.T) { + t.Log("Testing get root url with a valid url") + + // Test the get root url function with a valid url + url := "https://sub1.sub2.domain.com:8080" + expected := "sub2.domain.com" + + result, err := utils.GetRootURL(url) + + // Check if there was an error + if err != nil { + t.Fatalf("Error getting root url: %v", err) + } + + // Check if the result is equal to the expected + if expected != result { + t.Fatalf("Expected %v, got %v", expected, result) + } +} + +// Test the read file function +func TestReadFile(t *testing.T) { + t.Log("Creating a test file") + + // Create a test file + err := os.WriteFile("/tmp/test.txt", []byte("test"), 0644) + + // Check if there was an error + if err != nil { + t.Fatalf("Error creating test file: %v", err) + } + + // Test the read file function + t.Log("Testing read file with a valid file") + + data, err := utils.ReadFile("/tmp/test.txt") + + // Check if there was an error + if err != nil { + t.Fatalf("Error reading file: %v", err) + } + + // Check if the data is equal to the expected + if data != "test" { + t.Fatalf("Expected test, got %v", data) + } + + // Cleanup the test file + t.Log("Cleaning up test file") + + err = os.Remove("/tmp/test.txt") + + // Check if there was an error + if err != nil { + t.Fatalf("Error cleaning up test file: %v", err) + } +} + +// Test the parse file to line function +func TestParseFileToLine(t *testing.T) { + t.Log("Testing parse file to line with a valid string") + + // Test the parse file to line function with a valid string + content := "user1:pass1\nuser2:pass2" + expected := "user1:pass1,user2:pass2" + + result := utils.ParseFileToLine(content) + + // Check if the result is equal to the expected + if expected != result { + t.Fatalf("Expected %v, got %v", expected, result) + } +} + +// Test the get secret function +func TestGetSecret(t *testing.T) { + t.Log("Testing get secret with an empty config and file") + + // Test the get secret function with an empty config and file + conf := "" + file := "/tmp/test.txt" + expected := "test" + + // Create file + err := os.WriteFile(file, []byte(expected), 0644) + + // Check if there was an error + if err != nil { + t.Fatalf("Error creating test file: %v", err) + } + + // Test + result := utils.GetSecret(conf, file) + + // Check if the result is equal to the expected + if result != expected { + t.Fatalf("Expected %v, got %v", expected, result) + } + + t.Log("Testing get secret with an empty file and a valid config") + + // Test the get secret function with an empty file and a valid config + result = utils.GetSecret(expected, "") + + // Check if the result is equal to the expected + if result != expected { + t.Fatalf("Expected %v, got %v", expected, result) + } + + t.Log("Testing get secret with both a valid config and file") + + // Test the get secret function with both a valid config and file + result = utils.GetSecret(expected, file) + + // Check if the result is equal to the expected + if result != expected { + t.Fatalf("Expected %v, got %v", expected, result) + } + + // Cleanup the test file + t.Log("Cleaning up test file") + + err = os.Remove(file) + + // Check if there was an error + if err != nil { + t.Fatalf("Error cleaning up test file: %v", err) + } +} + +// Test the get users function +func TestGetUsers(t *testing.T) { + t.Log("Testing get users with a config and no file") + + // Test the get users function with a config and no file + conf := "user1:pass1,user2:pass2" + file := "" + expected := types.Users{ + { + Username: "user1", + Password: "pass1", + }, + { + Username: "user2", + Password: "pass2", + }, + } + + result, err := utils.GetUsers(conf, file) + + // Check if there was an error + if err != nil { + t.Fatalf("Error getting users: %v", err) + } + + // Check if the result is equal to the expected + if !reflect.DeepEqual(expected, result) { + t.Fatalf("Expected %v, got %v", expected, result) + } + + t.Log("Testing get users with a file and no config") + + // Test the get users function with a file and no config + conf = "" + file = "/tmp/test.txt" + expected = types.Users{ + { + Username: "user1", + Password: "pass1", + }, + { + Username: "user2", + Password: "pass2", + }, + } + + // Create file + err = os.WriteFile(file, []byte("user1:pass1\nuser2:pass2"), 0644) + + // Check if there was an error + if err != nil { + t.Fatalf("Error creating test file: %v", err) + } + + // Test + result, err = utils.GetUsers(conf, file) + + // Check if there was an error + if err != nil { + t.Fatalf("Error getting users: %v", err) + } + + // Check if the result is equal to the expected + if !reflect.DeepEqual(expected, result) { + t.Fatalf("Expected %v, got %v", expected, result) + } + + // Test the get users function with both a config and file + t.Log("Testing get users with both a config and file") + + conf = "user3:pass3" + expected = types.Users{ + { + Username: "user3", + Password: "pass3", + }, + { + Username: "user1", + Password: "pass1", + }, + { + Username: "user2", + Password: "pass2", + }, + } + + result, err = utils.GetUsers(conf, file) + + // Check if there was an error + if err != nil { + t.Fatalf("Error getting users: %v", err) + } + + // Check if the result is equal to the expected + if !reflect.DeepEqual(expected, result) { + t.Fatalf("Expected %v, got %v", expected, result) + } + + // Cleanup the test file + t.Log("Cleaning up test file") + + err = os.Remove(file) + + // Check if there was an error + if err != nil { + t.Fatalf("Error cleaning up test file: %v", err) + } +} + +// Test the tinyauth labels function +func TestGetTinyauthLabels(t *testing.T) { + t.Log("Testing get tinyauth labels with a valid map") + + // Test the get tinyauth labels function with a valid map + labels := map[string]string{ + "tinyauth.users": "user1,user2", + "tinyauth.oauth.whitelist": "user1,user2", + "random": "random", + } + + expected := types.TinyauthLabels{ + Users: []string{"user1", "user2"}, + OAuthWhitelist: []string{"user1", "user2"}, + } + + result := utils.GetTinyauthLabels(labels) + + // Check if the result is equal to the expected + if !reflect.DeepEqual(expected, result) { + t.Fatalf("Expected %v, got %v", expected, result) + } +}