diff --git a/.gitea/workflows/tests.yml b/.gitea/workflows/tests.yml new file mode 100644 index 0000000..c1ee431 --- /dev/null +++ b/.gitea/workflows/tests.yml @@ -0,0 +1,21 @@ +name: Run Go Tests + +on: + pull_request: + branches: [master] + +jobs: + test: + name: Run Tests + runs-on: ubuntu-latest + steps: + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v4 + with: + go-version: '1.22' + + - name: Run Tests + run: make test \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..8bb3093 --- /dev/null +++ b/Makefile @@ -0,0 +1,3 @@ +test: + @echo "Running tests for main and all packages" + go test ./... \ No newline at end of file diff --git a/README.md b/README.md index 7965f61..7d1753d 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Currently in development. - `ffmpeg` make sure libopus is included - `yt-dlp` -## Current testing steps +## Running locally Copy .env.example to .env @@ -14,4 +14,8 @@ Populate the discord bot keys `go run .` -type `!` in any channel in the discord while you're in a voice channel. \ No newline at end of file +type `!` in any channel in the discord while you're in a voice channel. + +## Running tests locally + +`make test` \ No newline at end of file diff --git a/pkg/validation/validation.go b/pkg/validation/validation.go new file mode 100644 index 0000000..60a7787 --- /dev/null +++ b/pkg/validation/validation.go @@ -0,0 +1,54 @@ +package validation + +import ( + "errors" + "fmt" + neturl "net/url" + "strings" +) + +// Errors +var ( + ErrNotAUrl = errors.New("not a URL") + ErrIncorrectProtocol = errors.New("incorrect url protocol") + ErrServiceUnsupported = errors.New("not a URL") +) + +// Music hosts that we support +var musicHosts = []string{ + "youtube.com", + "www.youtube.com", +} + +func IsUrl(url string) (bool, *neturl.URL) { + // If a URL has no scheme, this will fail. + // So we'll add one if not present + if !strings.Contains(url, "://") { + url = fmt.Sprintf("https://%s", url) + } + + parsed, err := neturl.ParseRequestURI(url) + + return (err == nil), parsed +} + +func IsMusicUrl(url string) (bool, error) { + isUrl, parsed := IsUrl(url) + + if !isUrl { + return false, ErrNotAUrl + } + + // If we have a scheme and it's not http/https we fail + if parsed.Scheme != "" && !strings.Contains(parsed.Scheme, "http") { + return false, ErrIncorrectProtocol + } + + for _, host := range musicHosts { + if host == parsed.Host { + return true, nil + } + } + + return false, ErrServiceUnsupported +} \ No newline at end of file diff --git a/pkg/validation/validation_test.go b/pkg/validation/validation_test.go new file mode 100644 index 0000000..519d0cc --- /dev/null +++ b/pkg/validation/validation_test.go @@ -0,0 +1,85 @@ +package validation + +import ( + "testing" +) + +func TestIsUrl(t *testing.T) { + is, _ := IsUrl("definitely not a url") + + if is { + t.Error("Non-url text detected as URL") + } + + is, _ = IsUrl("https://example.com") + + if !is { + t.Error("URL not detected as URL") + } +} + +func TestSchemeHandling(t *testing.T) { + // No scheme but valid url + is, _ := IsUrl("youtube.com") + + if !is { + t.Error("URL without scheme came back as not a url") + } + + // Preserve scheme + is, parsed := IsUrl("ftp://youtube.com") + + if !is { + t.Error("URL without scheme came back as not a url") + } + + if parsed.Scheme != "ftp" { + t.Error("URL scheme was replaced incorrectly") + } +} + +func TestSupportedMusicUrls(t *testing.T) { + // Test actual music url + for _, url := range musicHosts { + is, _ := IsMusicUrl(url) + + if !is { + t.Error("Supported service was detected as unsupported") + } + } +} + +func TestIsMusicUrlErrors(t *testing.T) { + // Not a URL + is, err := IsMusicUrl("not a url") + + if is { + t.Error("Non-URL was detected as url") + } + + if err != ErrNotAUrl { + t.Error("Incorrect error returned for Non-url link") + } + + // Incorrect protocol + is, err = IsMusicUrl("ssh://youtube.com") + + if is { + t.Error("Incorrect protocol was not caught") + } + + if err != ErrIncorrectProtocol { + t.Error("Incorrect error returned for incorrect protocol") + } + + // Unsupported service + is, err = IsMusicUrl("https://www.deezer.com/") + + if is { + t.Error("Unsupported service was not caught") + } + + if err != ErrServiceUnsupported { + t.Error("Unsupported service did not return the correct error") + } +} \ No newline at end of file