go-help/server_test.go

232 lines
6.2 KiB
Go
Raw Permalink Normal View History

// SPDX-Licence-Identifier: EUPL-1.2
package help
import (
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// testServer creates a test catalog with topics and returns an httptest.Server.
func testServer(t *testing.T) *httptest.Server {
t.Helper()
c := &Catalog{
topics: make(map[string]*Topic),
index: newSearchIndex(),
}
c.Add(&Topic{
ID: "getting-started",
Title: "Getting Started",
Content: "# Getting Started\n\nWelcome to the **guide**.\n\n## Installation\n\nInstall the tool.\n",
Tags: []string{"intro", "setup"},
Sections: []Section{
{ID: "getting-started", Title: "Getting Started", Level: 1},
{ID: "installation", Title: "Installation", Level: 2, Content: "Install the tool."},
},
Related: []string{"config"},
})
c.Add(&Topic{
ID: "config",
Title: "Configuration",
Content: "# Configuration\n\nConfigure your environment.\n",
Tags: []string{"setup"},
})
srv := NewServer(c, ":0")
return httptest.NewServer(srv)
}
func TestServer_HandleIndex_Good(t *testing.T) {
ts := testServer(t)
defer ts.Close()
resp, err := http.Get(ts.URL + "/")
require.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
assert.Contains(t, resp.Header.Get("Content-Type"), "text/html")
assert.Equal(t, "nosniff", resp.Header.Get("X-Content-Type-Options"))
var buf [64 * 1024]byte
n, _ := resp.Body.Read(buf[:])
body := string(buf[:n])
assert.Contains(t, body, "Getting Started")
assert.Contains(t, body, "Configuration")
}
func TestServer_HandleTopic_Good(t *testing.T) {
ts := testServer(t)
defer ts.Close()
resp, err := http.Get(ts.URL + "/topics/getting-started")
require.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
assert.Contains(t, resp.Header.Get("Content-Type"), "text/html")
var buf [64 * 1024]byte
n, _ := resp.Body.Read(buf[:])
body := string(buf[:n])
assert.Contains(t, body, "Getting Started")
assert.Contains(t, body, "<strong>guide</strong>")
}
func TestServer_HandleTopic_Bad_NotFound(t *testing.T) {
ts := testServer(t)
defer ts.Close()
resp, err := http.Get(ts.URL + "/topics/nonexistent")
require.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusNotFound, resp.StatusCode)
assert.Contains(t, resp.Header.Get("Content-Type"), "text/html")
}
func TestServer_HandleSearch_Good(t *testing.T) {
ts := testServer(t)
defer ts.Close()
resp, err := http.Get(ts.URL + "/search?q=install")
require.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
assert.Contains(t, resp.Header.Get("Content-Type"), "text/html")
var buf [64 * 1024]byte
n, _ := resp.Body.Read(buf[:])
body := string(buf[:n])
assert.Contains(t, body, "install")
}
func TestServer_HandleSearch_Bad_NoQuery(t *testing.T) {
ts := testServer(t)
defer ts.Close()
resp, err := http.Get(ts.URL + "/search")
require.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
}
func TestServer_HandleAPITopics_Good(t *testing.T) {
ts := testServer(t)
defer ts.Close()
resp, err := http.Get(ts.URL + "/api/topics")
require.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
assert.Contains(t, resp.Header.Get("Content-Type"), "application/json")
assert.Equal(t, "nosniff", resp.Header.Get("X-Content-Type-Options"))
var topics []Topic
require.NoError(t, json.NewDecoder(resp.Body).Decode(&topics))
assert.Len(t, topics, 2)
}
func TestServer_HandleAPITopic_Good(t *testing.T) {
ts := testServer(t)
defer ts.Close()
resp, err := http.Get(ts.URL + "/api/topics/getting-started")
require.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
assert.Contains(t, resp.Header.Get("Content-Type"), "application/json")
var topic Topic
require.NoError(t, json.NewDecoder(resp.Body).Decode(&topic))
assert.Equal(t, "Getting Started", topic.Title)
assert.Equal(t, "getting-started", topic.ID)
}
func TestServer_HandleAPITopic_Bad_NotFound(t *testing.T) {
ts := testServer(t)
defer ts.Close()
resp, err := http.Get(ts.URL + "/api/topics/nonexistent")
require.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusNotFound, resp.StatusCode)
assert.Contains(t, resp.Header.Get("Content-Type"), "application/json")
}
func TestServer_HandleAPISearch_Good(t *testing.T) {
ts := testServer(t)
defer ts.Close()
resp, err := http.Get(ts.URL + "/api/search?q=config")
require.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
assert.Contains(t, resp.Header.Get("Content-Type"), "application/json")
var results []SearchResult
require.NoError(t, json.NewDecoder(resp.Body).Decode(&results))
assert.NotEmpty(t, results)
}
func TestServer_HandleAPISearch_Bad_NoQuery(t *testing.T) {
ts := testServer(t)
defer ts.Close()
resp, err := http.Get(ts.URL + "/api/search")
require.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
assert.Contains(t, resp.Header.Get("Content-Type"), "application/json")
}
func TestServer_ContentTypeHeaders_Good(t *testing.T) {
ts := testServer(t)
defer ts.Close()
tests := []struct {
name string
path string
contentType string
}{
{"index HTML", "/", "text/html"},
{"topic HTML", "/topics/getting-started", "text/html"},
{"search HTML", "/search?q=test", "text/html"},
{"API topics JSON", "/api/topics", "application/json"},
{"API topic JSON", "/api/topics/getting-started", "application/json"},
{"API search JSON", "/api/search?q=test", "application/json"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
resp, err := http.Get(ts.URL + tt.path)
require.NoError(t, err)
defer resp.Body.Close()
assert.Contains(t, resp.Header.Get("Content-Type"), tt.contentType,
"Content-Type for %s should contain %s", tt.path, tt.contentType)
})
}
}
func TestNewServer_Good(t *testing.T) {
c := DefaultCatalog()
srv := NewServer(c, ":8080")
assert.NotNil(t, srv)
assert.Equal(t, ":8080", srv.addr)
assert.NotNil(t, srv.mux)
assert.Equal(t, c, srv.catalog)
}