mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-11-07 00:05:14 +00:00
110 lines
3.8 KiB
Go
110 lines
3.8 KiB
Go
package v2
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"path/filepath"
|
|
"sync/atomic"
|
|
"testing"
|
|
|
|
"github.com/1Panel-dev/1Panel/agent/app/model"
|
|
"github.com/1Panel-dev/1Panel/agent/constant"
|
|
"github.com/1Panel-dev/1Panel/agent/global"
|
|
"github.com/1Panel-dev/1Panel/agent/utils/appicon"
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/glebarez/sqlite"
|
|
"github.com/sirupsen/logrus"
|
|
"gorm.io/gorm"
|
|
"gorm.io/gorm/logger"
|
|
)
|
|
|
|
func TestAppIconEndpointPreservesRemoteAndHistoricalSources(t *testing.T) {
|
|
db, err := gorm.Open(sqlite.Open(filepath.Join(t.TempDir(), "icons.db")), &gorm.Config{
|
|
Logger: logger.Default.LogMode(logger.Silent),
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := db.AutoMigrate(&model.App{}, &model.AppDetail{}, &model.AppTag{}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
oldDB, oldDir, oldLog := global.DB, global.Dir, global.LOG
|
|
global.DB = db
|
|
global.Dir.IconCacheDir = t.TempDir()
|
|
global.LOG = logrus.New()
|
|
global.LOG.SetOutput(io.Discard)
|
|
t.Cleanup(func() {
|
|
global.DB, global.Dir, global.LOG = oldDB, oldDir, oldLog
|
|
sqlDB, _ := db.DB()
|
|
_ = sqlDB.Close()
|
|
})
|
|
|
|
var imageRequests atomic.Int32
|
|
cdn := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
imageRequests.Add(1)
|
|
w.WriteHeader(http.StatusServiceUnavailable)
|
|
}))
|
|
t.Cleanup(cdn.Close)
|
|
remoteURL := cdn.URL + "/icon.png?token=a%2Bb&v=42#image"
|
|
image := "\x89PNG\r\n\x1a\noriginal-icon"
|
|
fileName := "app_php.png"
|
|
if err := os.WriteFile(filepath.Join(global.Dir.IconCacheDir, fileName), []byte(image), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
app := model.App{Key: "php", Resource: constant.AppResourceRemote}
|
|
if err := db.Create(&app).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
router := gin.New()
|
|
router.GET("/api/v2/apps/icon/:key", (&BaseApi{}).GetAppIcon)
|
|
for _, tc := range []struct {
|
|
name string
|
|
icon string
|
|
ifNoneMatch string
|
|
status int
|
|
body string
|
|
etag string
|
|
}{
|
|
{"remote URL", remoteURL, "", http.StatusFound, "", ""},
|
|
{"remote URL ignores legacy etag", remoteURL, `"old"`, http.StatusFound, "", ""},
|
|
{"historical file", appicon.BuildIconField(fileName, `"42"`), "", http.StatusOK, image, `"42"`},
|
|
{"historical file cached", appicon.BuildIconField(fileName, `"42"`), `"42"`, http.StatusNotModified, "", `"42"`},
|
|
{"historical file changed", appicon.BuildIconField(fileName, `"42"`), `"old"`, http.StatusOK, image, `"42"`},
|
|
{"historical base64", base64.StdEncoding.EncodeToString([]byte(image)), "", http.StatusOK, image, ""},
|
|
{"missing file", "app_missing.png?etag=old", "", http.StatusNoContent, "", ""},
|
|
{"invalid base64", "not-an-icon", "", http.StatusNoContent, "", ""},
|
|
{"invalid URL", "javascript:alert(1)", "", http.StatusNoContent, "", ""},
|
|
{"empty icon", "", "", http.StatusNoContent, "", ""},
|
|
} {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
if err := db.Model(&app).UpdateColumn("icon", tc.icon).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
req := httptest.NewRequest(http.MethodGet, "/api/v2/apps/icon/php?operateNode=child", nil)
|
|
req.Header.Set("If-None-Match", tc.ifNoneMatch)
|
|
res := httptest.NewRecorder()
|
|
router.ServeHTTP(res, req)
|
|
if res.Code != tc.status {
|
|
t.Fatalf("status = %d, want %d; body = %s", res.Code, tc.status, res.Body.String())
|
|
}
|
|
if res.Header().Get("ETag") != tc.etag {
|
|
t.Fatalf("etag = %q, want %q", res.Header().Get("ETag"), tc.etag)
|
|
}
|
|
if tc.status == http.StatusFound {
|
|
if res.Header().Get("Location") != remoteURL || res.Header().Get("Cache-Control") != "no-cache" {
|
|
t.Fatalf("redirect URL or cache policy was changed: %v", res.Header())
|
|
}
|
|
} else if res.Body.String() != tc.body {
|
|
t.Fatalf("body = %q, want %q", res.Body.String(), tc.body)
|
|
}
|
|
if imageRequests.Load() != 0 {
|
|
t.Fatalf("icon endpoint fetched the image %d times", imageRequests.Load())
|
|
}
|
|
})
|
|
}
|
|
}
|