feat: clanker fixxes + postgress
This commit is contained in:
@@ -0,0 +1,171 @@
|
||||
package routes
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io/fs"
|
||||
"kooltap-updateserver/internal/db"
|
||||
"kooltap-updateserver/internal/models"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/glebarez/sqlite"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/logger"
|
||||
)
|
||||
|
||||
func newHandler(t *testing.T, releases ...models.Release) (*Handler, *gin.Engine) {
|
||||
t.Helper()
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
database, err := gorm.Open(sqlite.Open("file:"+t.Name()+"?mode=memory&cache=shared"), &gorm.Config{
|
||||
TranslateError: true,
|
||||
Logger: logger.Discard,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := db.Migrate(database); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for i := range releases {
|
||||
if err := database.Create(&releases[i]).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
h := &Handler{DB: database, UploadDir: t.TempDir(), TmpDir: t.TempDir()}
|
||||
r := gin.New()
|
||||
h.Register(r)
|
||||
return h, r
|
||||
}
|
||||
|
||||
func do(r *gin.Engine, method, url string) (*httptest.ResponseRecorder, map[string]any) {
|
||||
req := httptest.NewRequest(method, url, nil)
|
||||
req.Header.Set("X-Admin-Token", "secret")
|
||||
w := httptest.NewRecorder()
|
||||
r.ServeHTTP(w, req)
|
||||
var body map[string]any
|
||||
_ = json.Unmarshal(w.Body.Bytes(), &body)
|
||||
return w, body
|
||||
}
|
||||
|
||||
func TestCheckUpdate(t *testing.T) {
|
||||
_, r := newHandler(t,
|
||||
models.Release{Version: "1.2.0", VersionCode: 12, APKFile: "app-1.2.0.apk"},
|
||||
models.Release{Version: "1.10.0", VersionCode: 110, APKFile: "app-1.10.0.apk", Mandatory: true},
|
||||
)
|
||||
|
||||
cases := []struct {
|
||||
query string
|
||||
want bool
|
||||
}{
|
||||
{"version=1.2.0", true},
|
||||
{"version=1.10.0", false},
|
||||
{"version=2.0.0", false},
|
||||
{"version=1.2.0&code=12", true},
|
||||
{"version=1.10.0&code=110", false},
|
||||
{"version=garbage", true},
|
||||
{"version=garbage&code=200", false},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.query, func(t *testing.T) {
|
||||
w, body := do(r, http.MethodGet, "/api/update?"+tc.query)
|
||||
if w.Code != http.StatusOK {
|
||||
t.Fatalf("status %d", w.Code)
|
||||
}
|
||||
if body["update"] != tc.want {
|
||||
t.Fatalf("update = %v, want %v", body["update"], tc.want)
|
||||
}
|
||||
if tc.want && body["download"] != "/downloads/app-1.10.0.apk" {
|
||||
t.Fatalf("download = %v", body["download"])
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckUpdateNoReleases(t *testing.T) {
|
||||
_, r := newHandler(t)
|
||||
_, body := do(r, http.MethodGet, "/api/update?version=1.0.0")
|
||||
if body["update"] != false {
|
||||
t.Fatalf("update = %v", body["update"])
|
||||
}
|
||||
if w, _ := do(r, http.MethodGet, "/download/latest"); w.Code != http.StatusNotFound {
|
||||
t.Fatalf("download latest status %d", w.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestListReleasesEmptyIsArray(t *testing.T) {
|
||||
_, r := newHandler(t)
|
||||
w, _ := do(r, http.MethodGet, "/api/releases")
|
||||
if w.Body.String() != "[]" {
|
||||
t.Fatalf("body = %s", w.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteRelease(t *testing.T) {
|
||||
t.Setenv("ADMIN_TOKEN", "secret")
|
||||
h, r := newHandler(t, models.Release{Version: "1.0.0", VersionCode: 1, APKFile: "app-1.0.0.apk"})
|
||||
|
||||
apk := filepath.Join(h.UploadDir, "app-1.0.0.apk")
|
||||
if err := os.WriteFile(apk, []byte("x"), 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if w, _ := do(r, http.MethodDelete, "/admin/releases/abc"); w.Code != http.StatusNotFound {
|
||||
t.Fatalf("bad id status %d", w.Code)
|
||||
}
|
||||
if w, _ := do(r, http.MethodDelete, "/admin/releases/1"); w.Code != http.StatusOK {
|
||||
t.Fatalf("delete status %d", w.Code)
|
||||
}
|
||||
if _, err := os.Stat(apk); !errors.Is(err, fs.ErrNotExist) {
|
||||
t.Fatalf("apk still exists: %v", err)
|
||||
}
|
||||
if w, _ := do(r, http.MethodDelete, "/admin/releases/1"); w.Code != http.StatusNotFound {
|
||||
t.Fatalf("second delete status %d", w.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMoveFileNeverOverwrites(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
src := filepath.Join(dir, "src")
|
||||
dst := filepath.Join(dir, "dst")
|
||||
os.WriteFile(src, []byte("new"), 0644)
|
||||
os.WriteFile(dst, []byte("old"), 0644)
|
||||
|
||||
if err := moveFile(src, dst); !errors.Is(err, fs.ErrExist) {
|
||||
t.Fatalf("err = %v, want ErrExist", err)
|
||||
}
|
||||
if b, _ := os.ReadFile(dst); string(b) != "old" {
|
||||
t.Fatalf("dst overwritten: %q", b)
|
||||
}
|
||||
|
||||
os.Remove(dst)
|
||||
if err := moveFile(src, dst); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if b, _ := os.ReadFile(dst); string(b) != "new" {
|
||||
t.Fatalf("dst = %q", b)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSafeName(t *testing.T) {
|
||||
cases := map[string]string{
|
||||
"1.2.3": "1.2.3",
|
||||
"1.0.0-beta+7": "1.0.0-beta+7",
|
||||
"../../etc/x": ".._.._etc_x",
|
||||
"..": "_",
|
||||
"": "_",
|
||||
`1.0\..\evil`: "1.0_.._evil",
|
||||
"1.0 (release)": "1.0__release_",
|
||||
}
|
||||
for in, want := range cases {
|
||||
if got := safeName(in); got != want {
|
||||
t.Errorf("safeName(%q) = %q, want %q", in, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user