//go:build embed package web import ( "bytes" "context" "net/http" "net/http/httptest" "strings" "testing" "github.com/Wei-Shaw/sub2api/internal/server/middleware" "github.com/gin-gonic/gin" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func init() { gin.SetMode(gin.TestMode) } func TestReplaceNoncePlaceholder(t *testing.T) { t.Run("replaces_single_placeholder", func(t *testing.T) { html := []byte(``) nonce := "abc123xyz" result := replaceNoncePlaceholder(html, nonce) expected := `` assert.Equal(t, expected, string(result)) }) t.Run("replaces_multiple_placeholders", func(t *testing.T) { html := []byte(``) nonce := "nonce123" result := replaceNoncePlaceholder(html, nonce) assert.Equal(t, 2, strings.Count(string(result), `nonce="nonce123"`)) assert.NotContains(t, string(result), NonceHTMLPlaceholder) }) t.Run("handles_empty_nonce", func(t *testing.T) { html := []byte(``) nonce := "" result := replaceNoncePlaceholder(html, nonce) assert.Equal(t, ``, string(result)) }) t.Run("no_placeholder_returns_unchanged", func(t *testing.T) { html := []byte(``) nonce := "abc123" result := replaceNoncePlaceholder(html, nonce) assert.Equal(t, string(html), string(result)) }) t.Run("handles_empty_html", func(t *testing.T) { html := []byte(``) nonce := "abc123" result := replaceNoncePlaceholder(html, nonce) assert.Empty(t, result) }) } func TestNonceHTMLPlaceholder(t *testing.T) { t.Run("constant_value", func(t *testing.T) { assert.Equal(t, "__CSP_NONCE_VALUE__", NonceHTMLPlaceholder) }) } // mockSettingsProvider implements PublicSettingsProvider for testing type mockSettingsProvider struct { settings any err error called int } func (m *mockSettingsProvider) GetPublicSettingsForInjection(ctx context.Context) (any, error) { m.called++ return m.settings, m.err } func TestFrontendServer_InjectSettings(t *testing.T) { t.Run("injects_settings_with_nonce_placeholder", func(t *testing.T) { provider := &mockSettingsProvider{ settings: map[string]string{"key": "value"}, } server, err := NewFrontendServer(provider) require.NoError(t, err) settingsJSON := []byte(`{"test":"data"}`) result := server.injectSettings(settingsJSON) // Should contain the script with nonce placeholder assert.Contains(t, string(result), ``) }) t.Run("injects_before_head_close", func(t *testing.T) { provider := &mockSettingsProvider{ settings: map[string]string{"key": "value"}, } server, err := NewFrontendServer(provider) require.NoError(t, err) settingsJSON := []byte(`{}`) result := server.injectSettings(settingsJSON) // Script should be injected before headCloseIndex := bytes.Index(result, []byte("")) scriptIndex := bytes.Index(result, []byte(``) nonce := "abcdefghijklmnop123456==" b.ResetTimer() for i := 0; i < b.N; i++ { replaceNoncePlaceholder(html, nonce) } } func BenchmarkFrontendServerServeIndexHTML(b *testing.B) { provider := &mockSettingsProvider{ settings: map[string]string{"test": "value"}, } server, _ := NewFrontendServer(provider) b.ResetTimer() for i := 0; i < b.N; i++ { w := httptest.NewRecorder() c, _ := gin.CreateTestContext(w) c.Request = httptest.NewRequest(http.MethodGet, "/", nil) c.Set(middleware.CSPNonceKey, "test-nonce") server.serveIndexHTML(c) } }