From d9be5a78c8c1b6940e1a5daeb3362a1d6adf5f2b Mon Sep 17 00:00:00 2001
From: Javier Feliz <me@javierfeliz.com>
Date: Sun, 9 Feb 2025 00:25:41 -0500
Subject: [PATCH] Add validation package

---
 .gitea/workflows/tests.yml        | 21 +++++++++++++
 Makefile                          |  3 ++
 pkg/validation/validation.go      | 46 +++++++++++++++++++++++++++
 pkg/validation/validation_test.go | 52 +++++++++++++++++++++++++++++++
 4 files changed, 122 insertions(+)
 create mode 100644 .gitea/workflows/tests.yml
 create mode 100644 Makefile
 create mode 100644 pkg/validation/validation.go
 create mode 100644 pkg/validation/validation_test.go

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/pkg/validation/validation.go b/pkg/validation/validation.go
new file mode 100644
index 0000000..58b9b80
--- /dev/null
+++ b/pkg/validation/validation.go
@@ -0,0 +1,46 @@
+package validation
+
+import (
+	"errors"
+	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",
+}
+
+func IsUrl(url string) (bool, *neturl.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..3c7c762
--- /dev/null
+++ b/pkg/validation/validation_test.go
@@ -0,0 +1,52 @@
+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 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