Files
golib/static/static_suite_test.go
nabbar 43241f78ba [ file/progress ]
- ADD flag to register temp file creation
    - ADD function to check flag is temp

[ static ]
    - FIX bugs & race detection
    - UPDATE code: refactor & optimize code, improve security &
      preformances
    - ADD Path Security: add options & code to improve security
    - ADD Rate Limiting: add option to limit capabilities of burst request
    - ADD HTTP Security Headers: add option to customize header, improve
      security & allow cache crontol
    - ADD Suspicious Access Detection: add option to identify & log
      suspicious request
    - ADD Security Backend Integration: add option to plug WAF/IDF/EDR
      backend (with CEF Format or not)
    - ADD documentation: add enhanced README and TESTING guidelines
    - ADD tests: complete test suites with benchmarks, concurrency, and edge cases
2025-11-23 19:30:27 +01:00

325 lines
7.8 KiB
Go

/*
* MIT License
*
* Copyright (c) 2022 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package static_test
import (
"context"
"embed"
"io"
"net/http"
"net/http/httptest"
"os"
"strconv"
"testing"
ginsdk "github.com/gin-gonic/gin"
libdur "github.com/nabbar/golib/duration"
libfpg "github.com/nabbar/golib/file/progress"
liblog "github.com/nabbar/golib/logger"
logcfg "github.com/nabbar/golib/logger/config"
montps "github.com/nabbar/golib/monitor/types"
librtr "github.com/nabbar/golib/router"
"github.com/nabbar/golib/static"
libver "github.com/nabbar/golib/version"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
//go:embed testdata/*
var testFS embed.FS
var (
// Global context for all tests
testCtx context.Context
testCancel context.CancelFunc
// Test gin engine
testGinEngine *ginsdk.Engine
// Global logger for tests
testLogger liblog.Logger
testAccLog liblog.Logger
)
type staticDownload interface {
SetDownload(string, bool)
IsDownload(string) bool
}
type staticIndex interface {
SetIndex(string, string, string)
GetIndex(string, string) string
IsIndex(string) bool
IsIndexForRoute(string, string, string) bool
}
type staticRedirect interface {
SetRedirect(string, string, string, string)
GetRedirect(string, string) string
IsRedirect(string, string) bool
}
type staticSpecific interface {
SetSpecific(string, string, ginsdk.HandlerFunc)
GetSpecific(string, string) ginsdk.HandlerFunc
}
type staticConfig interface {
SetDownload(string, bool)
SetIndex(string, string, string)
IsDownload(string) bool
IsIndex(string) bool
GetIndex(string, string) string
}
type staticMap interface {
Map(func(string, os.FileInfo) error) error
}
type staticFind interface {
Find(string) (io.ReadCloser, error)
}
type staticList interface {
List(rootPath string) ([]string, error)
}
type staticInfo interface {
Info(string) (os.FileInfo, error)
}
type staticTemp interface {
Temp(string) (libfpg.Progress, error)
}
type staticTempSize interface {
UseTempForFileSize(int64)
}
type staticFindHas interface {
Find(string) (io.ReadCloser, error)
Has(string) bool
}
type staticFindTempSize interface {
Find(string) (io.ReadCloser, error)
UseTempForFileSize(int64)
}
// TestStatic is the entry point for the Ginkgo test suite
func TestStatic(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Static Package Suite")
}
var _ = BeforeSuite(func() {
testCtx, testCancel = context.WithCancel(context.Background())
ginsdk.SetMode(ginsdk.TestMode)
// Initialize logger
testLogger = liblog.New(testCtx)
Expect(testLogger.SetOptions(&logcfg.Options{
Stdout: &logcfg.OptionsStd{
DisableStandard: true, // Enable log output for security evt
},
})).ToNot(HaveOccurred())
// Initialize logger
testAccLog = liblog.New(testCtx)
Expect(testAccLog.SetOptions(&logcfg.Options{
Stdout: &logcfg.OptionsStd{
EnableAccessLog: true,
DisableStandard: true, // Enable log output for access logs
},
})).ToNot(HaveOccurred())
})
var _ = AfterSuite(func() {
// Close access logger
if testAccLog != nil {
_ = testAccLog.Close()
}
// Close logger
if testLogger != nil {
_ = testLogger.Close()
}
if testCancel != nil {
testCancel()
}
})
// Helper functions
// newTestVersion creates a test version instance for use in tests
func newTestVersion() libver.Version {
return libver.NewVersion(
libver.License_MIT,
"static-test",
"Static Package Test",
"2024",
"test-build",
"1.0.0",
"test-author",
"",
struct{}{},
0,
)
}
// newTestMonitorConfig creates a test monitor config instance
func newTestMonitorConfig() montps.Config {
return montps.Config{
Name: "test",
CheckTimeout: libdur.Seconds(1),
IntervalCheck: libdur.Seconds(1),
IntervalFall: libdur.Seconds(1),
IntervalRise: libdur.Seconds(1),
FallCountKO: 1,
FallCountWarn: 1,
RiseCountKO: 1,
RiseCountWarn: 1,
Logger: logcfg.Options{
Stdout: &logcfg.OptionsStd{
DisableStandard: true,
},
},
}
}
// getTestLogger returns the global test logger
func getTestLogger() liblog.Logger {
return testLogger
}
// getTestLogger returns the global test logger
func getTestAccessLogger() liblog.Logger {
return testAccLog
}
// newTestStatic creates a new static handler with test data
func newTestStatic() static.Static {
s := static.New(testCtx, testFS, "testdata")
s.RegisterLogger(testLogger)
return s
}
// newTestStaticWithRoot creates a new static handler with custom root
func newTestStaticWithRoot(roots ...string) static.Static {
s := static.New(testCtx, testFS, roots...)
s.RegisterLogger(testLogger)
return s
}
// setupTestRouter creates a gin router with static handler registered
func setupTestRouter(handler static.Static, route string, middlewares ...ginsdk.HandlerFunc) *ginsdk.Engine {
n, e := librtr.GinEngine("")
Expect(e).NotTo(HaveOccurred())
// Disable automatic trailing slash redirects for tests
n.RedirectTrailingSlash = false
n = librtr.GinAddGlobalMiddleware(n, librtr.GinAccessLog(func() liblog.Logger {
return getTestAccessLogger()
}))
n = librtr.GinAddGlobalMiddleware(n, librtr.GinErrorLog(func() liblog.Logger {
return getTestLogger()
}))
routerList := librtr.NewRouterList(func() *ginsdk.Engine {
return n
})
handler.RegisterRouter(route, routerList.Register, middlewares...)
routerList.Handler(n)
return n
}
// setupTestRouterInGroup creates a gin router with static handler registered in a group
func setupTestRouterInGroup(handler static.Static, route, group string, middlewares ...ginsdk.HandlerFunc) *ginsdk.Engine {
routerList := librtr.NewRouterList(librtr.DefaultGinInit)
handler.RegisterRouterInGroup(route, group, routerList.RegisterInGroup, middlewares...)
engine := routerList.Engine()
// Disable automatic trailing slash redirects for tests
engine.RedirectTrailingSlash = false
routerList.Handler(engine)
return engine
}
// performRequest performs an HTTP request and returns the response
func performRequest(engine *ginsdk.Engine, method, path string) *httptest.ResponseRecorder {
req, err := http.NewRequest(method, path, nil)
Expect(err).ToNot(HaveOccurred())
w := httptest.NewRecorder()
engine.ServeHTTP(w, req)
return w
}
// testMiddleware is a simple middleware for testing
func testMiddleware(ctx *ginsdk.Context) {
ctx.Header("X-Test-Middleware", "true")
ctx.Next()
}
func newMiddleware(id int) ginsdk.HandlerFunc {
var h = "X-Test-Middleware"
if id > 0 {
h = h + "-" + strconv.Itoa(id)
}
return func(ctx *ginsdk.Context) {
ctx.Header(h, "true")
ctx.Next()
}
}
func customMiddlewareOK(response string, fct func()) ginsdk.HandlerFunc {
if fct == nil {
fct = func() {}
}
return func(c *ginsdk.Context) {
fct()
c.String(http.StatusOK, response)
}
}
func customMiddlewareCreated(response string) ginsdk.HandlerFunc {
return func(c *ginsdk.Context) {
c.String(http.StatusCreated, response)
}
}