<commit_before>// SPDX-License-Identifier: MIT

package server

import (
	"context"
	"crypto/tls"
	"encoding/json"
	"encoding/xml"
	"io/fs"
	"net"
	"net/http"
	"net/http/httptest"
	"os"
	"testing"
	"time"

	"github.com/issue9/assert"
	"github.com/issue9/assert/rest"
	"github.com/issue9/logs/v2"
	"github.com/issue9/mux/v5/group"
	"golang.org/x/text/language"
	"golang.org/x/text/message"

	"github.com/issue9/web/content"
	"github.com/issue9/web/content/gob"
	"github.com/issue9/web/content/text"
)

var _ fs.FS = &Server{}

var f201 = func(ctx *Context) {
	ctx.Response.Header().Set("Content-Type", "text/html")
	ctx.Response.WriteHeader(http.StatusCreated)
	_, err := ctx.Response.Write([]byte("1234567890"))
	if err != nil {
		println(err)
	}
}

var f202 = func(ctx *Context) {
	ctx.Response.WriteHeader(http.StatusAccepted)
	_, err := ctx.Response.Write([]byte("1234567890"))
	if err != nil {
		println(err)
	}
}

func newLogs(a *assert.Assertion) *logs.Logs {
	l := logs.New()

	a.NotError(l.SetOutput(logs.LevelDebug, os.Stderr, "", 0))
	a.NotError(l.SetOutput(logs.LevelError, os.Stderr, "", 0))
	a.NotError(l.SetOutput(logs.LevelCritical, os.Stderr, "", 0))
	return l
}

// 声明一个 server 实例
func newServer(a *assert.Assertion) *Server {
	srv, err := New("app", "0.1.0", newLogs(a), &Options{Port: ":8080"})
	a.NotError(err).NotNil(srv)
	a.Equal(srv.Name(), "app").Equal(srv.Version(), "0.1.0")

	// srv.Catalog 默认指向 message.DefaultCatalog
	a.NotError(message.SetString(language.Und, "lang", "und"))
	a.NotError(message.SetString(language.SimplifiedChinese, "lang", "hans"))
	a.NotError(message.SetString(language.TraditionalChinese, "lang", "hant"))

	mt := srv.Content()
	a.NotError(mt.AddMimetype("application/json", json.Marshal, json.Unmarshal))
	a.NotError(mt.AddMimetype("application/xml", xml.Marshal, xml.Unmarshal))
	a.NotError(mt.AddMimetype(content.DefaultMimetype, gob.Marshal, gob.Unmarshal))
	a.NotError(mt.AddMimetype(text.Mimetype, text.Marshal, text.Unmarshal))

	srv.Content().AddResult(411, 41110, "41110")

	return srv
}

func TestNewServer(t *testing.T) {
	a := assert.New(t)
	l := newLogs(a)

	srv, err := New("app", "0.1.0", l, nil)
	a.NotError(err).NotNil(srv)
	a.False(srv.Uptime().IsZero())
	a.Equal(l, srv.Logs())
	a.NotNil(srv.Cache())
	a.Equal(srv.Location(), time.Local)
	a.Equal(srv.httpServer.Handler, srv.groups)
	a.NotNil(srv.httpServer.BaseContext)
	a.Equal(srv.httpServer.Addr, "")
}

func TestGetServer(t *testing.T) {
	a := assert.New(t)
	type key int
	var k key = 0

	srv, err := New("app", "0.1.0", newLogs(a), nil)
	a.NotError(err).NotNil(srv)
	a.NotError(srv.content.AddMimetype(text.Mimetype, text.Marshal, text.Unmarshal))
	a.NotError(srv.content.AddMimetype(content.DefaultMimetype, text.Marshal, text.Unmarshal))
	var isRequested bool

	router, err := srv.NewRouter("default", "http://localhost:8081/", group.MatcherFunc(group.Any))
	a.NotError(err).NotNil(router)
	router.MuxRouter().GetFunc("/path", func(w http.ResponseWriter, r *http.Request) {
		s1 := GetServer(r)
		a.NotNil(s1).Equal(s1, srv)

		v := r.Context().Value(k)
		a.Nil(v)

		ctx1 := NewContext(w, r)
		a.NotNil(ctx1)
		ctx2 := NewContext(w, ctx1.Request)
		a.Equal(ctx1, ctx2)

		isRequested = true
	})

	go func() {
		a.Equal(srv.Serve(), http.ErrServerClosed)
	}()
	time.Sleep(500 * time.Millisecond)
	rest.NewRequest(a, nil, http.MethodGet, "http://localhost/path").
		Header("Accept", text.Mimetype).
		Do().
		Status(200)
		//Success("未正确返回状态码")

	// 不是从 Server 生成的 *http.Request，则会 panic
	r := httptest.NewRequest(http.MethodGet, "/path", nil)
	a.Panic(func() {
		GetServer(r)
	})

	a.NotError(srv.Close(0))
	a.True(isRequested, "未正常访问 /path")

	// BaseContext

	srv, err = New("app", "0.1.0", newLogs(a), &Options{
		HTTPServer: func(s *http.Server) {
			s.BaseContext = func(n net.Listener) context.Context {
				return context.WithValue(context.Background(), k, 1)
			}
		},
	})
	a.NotError(err).NotNil(srv)

	isRequested = false
	router, err = srv.NewRouter("default", "http://localhost:8080/", group.MatcherFunc(group.Any))
	a.NotError(err).NotNil(router)
	router.MuxRouter().GetFunc("/path", func(w http.ResponseWriter, r *http.Request) {
		s1 := GetServer(r)
		a.NotNil(s1).Equal(s1, srv)

		v := r.Context().Value(k) // BaseContext 中设置了 k 的值
		a.Equal(v, 1)

		isRequested = true
	})
	go func() {
		a.Equal(srv.Serve(), http.ErrServerClosed)
	}()
	time.Sleep(500 * time.Millisecond)
	rest.NewRequest(a, nil, http.MethodGet, "http://localhost/path").Do().Success()
	a.NotError(srv.Close(0))
	a.True(isRequested, "未正常访问 /path")
}

func TestServer_vars(t *testing.T) {
	a := assert.New(t)
	srv := newServer(a)

	type (
		t1 int
		t2 int64
		t3 = t2
	)
	var (
		v1 t1 = 1
		v2 t2 = 1
		v3 t3 = 1
	)

	srv.Set(v1, 1)
	srv.Set(v2, 2)
	srv.Set(v3, 3)

	v11, found := srv.Get(v1)
	a.True(found).Equal(v11, 1)
	v22, found := srv.Get(v2)
	a.True(found).Equal(v22, 3)<commit_msg>test(server): 防止部分测试在 80 端口不可用的情况<commit_after>// SPDX-License-Identifier: MIT

package server

import (
	"context"
	"crypto/tls"
	"encoding/json"
	"encoding/xml"
	"io/fs"
	"net"
	"net/http"
	"net/http/httptest"
	"os"
	"testing"
	"time"

	"github.com/issue9/assert"
	"github.com/issue9/assert/rest"
	"github.com/issue9/logs/v2"
	"github.com/issue9/mux/v5/group"
	"golang.org/x/text/language"
	"golang.org/x/text/message"

	"github.com/issue9/web/content"
	"github.com/issue9/web/content/gob"
	"github.com/issue9/web/content/text"
)

var _ fs.FS = &Server{}

var f201 = func(ctx *Context) {
	ctx.Response.Header().Set("Content-Type", "text/html")
	ctx.Response.WriteHeader(http.StatusCreated)
	_, err := ctx.Response.Write([]byte("1234567890"))
	if err != nil {
		println(err)
	}
}

var f202 = func(ctx *Context) {
	ctx.Response.WriteHeader(http.StatusAccepted)
	_, err := ctx.Response.Write([]byte("1234567890"))
	if err != nil {
		println(err)
	}
}

func newLogs(a *assert.Assertion) *logs.Logs {
	l := logs.New()

	a.NotError(l.SetOutput(logs.LevelDebug, os.Stderr, "", 0))
	a.NotError(l.SetOutput(logs.LevelError, os.Stderr, "", 0))
	a.NotError(l.SetOutput(logs.LevelCritical, os.Stderr, "", 0))
	return l
}

// 声明一个 server 实例
func newServer(a *assert.Assertion) *Server {
	srv, err := New("app", "0.1.0", newLogs(a), &Options{Port: ":8080"})
	a.NotError(err).NotNil(srv)
	a.Equal(srv.Name(), "app").Equal(srv.Version(), "0.1.0")

	// srv.Catalog 默认指向 message.DefaultCatalog
	a.NotError(message.SetString(language.Und, "lang", "und"))
	a.NotError(message.SetString(language.SimplifiedChinese, "lang", "hans"))
	a.NotError(message.SetString(language.TraditionalChinese, "lang", "hant"))

	mt := srv.Content()
	a.NotError(mt.AddMimetype("application/json", json.Marshal, json.Unmarshal))
	a.NotError(mt.AddMimetype("application/xml", xml.Marshal, xml.Unmarshal))
	a.NotError(mt.AddMimetype(content.DefaultMimetype, gob.Marshal, gob.Unmarshal))
	a.NotError(mt.AddMimetype(text.Mimetype, text.Marshal, text.Unmarshal))

	srv.Content().AddResult(411, 41110, "41110")

	return srv
}

func TestNewServer(t *testing.T) {
	a := assert.New(t)
	l := newLogs(a)

	srv, err := New("app", "0.1.0", l, nil)
	a.NotError(err).NotNil(srv)
	a.False(srv.Uptime().IsZero())
	a.Equal(l, srv.Logs())
	a.NotNil(srv.Cache())
	a.Equal(srv.Location(), time.Local)
	a.Equal(srv.httpServer.Handler, srv.groups)
	a.NotNil(srv.httpServer.BaseContext)
	a.Equal(srv.httpServer.Addr, "")
}

func TestGetServer(t *testing.T) {
	a := assert.New(t)
	type key int
	var k key = 0

	srv, err := New("app", "0.1.0", newLogs(a), &Options{Port: ":8080"})
	a.NotError(err).NotNil(srv)
	a.NotError(srv.content.AddMimetype(text.Mimetype, text.Marshal, text.Unmarshal))
	a.NotError(srv.content.AddMimetype(content.DefaultMimetype, text.Marshal, text.Unmarshal))
	var isRequested bool

	router, err := srv.NewRouter("default", "http://localhost:8081/", group.MatcherFunc(group.Any))
	a.NotError(err).NotNil(router)
	router.MuxRouter().GetFunc("/path", func(w http.ResponseWriter, r *http.Request) {
		s1 := GetServer(r)
		a.NotNil(s1).Equal(s1, srv)

		v := r.Context().Value(k)
		a.Nil(v)

		ctx1 := NewContext(w, r)
		a.NotNil(ctx1)
		ctx2 := NewContext(w, ctx1.Request)
		a.Equal(ctx1, ctx2)

		isRequested = true
	})

	go func() {
		a.Equal(srv.Serve(), http.ErrServerClosed)
	}()
	time.Sleep(500 * time.Millisecond)
	rest.NewRequest(a, nil, http.MethodGet, "http://localhost:8080/path").
		Header("Accept", text.Mimetype).
		Do().
		Status(200)
		//Success("未正确返回状态码")

	// 不是从 Server 生成的 *http.Request，则会 panic
	r := httptest.NewRequest(http.MethodGet, "/path", nil)
	a.Panic(func() {
		GetServer(r)
	})

	a.NotError(srv.Close(0))
	a.True(isRequested, "未正常访问 /path")

	// BaseContext

	srv, err = New("app", "0.1.0", newLogs(a), &Options{
		Port: ":8080",
		HTTPServer: func(s *http.Server) {
			s.BaseContext = func(n net.Listener) context.Context {
				return context.WithValue(context.Background(), k, 1)
			}
		},
	})
	a.NotError(err).NotNil(srv)

	isRequested = false
	router, err = srv.NewRouter("default", "http://localhost:8080/", group.MatcherFunc(group.Any))
	a.NotError(err).NotNil(router)
	router.MuxRouter().GetFunc("/path", func(w http.ResponseWriter, r *http.Request) {
		s1 := GetServer(r)
		a.NotNil(s1).Equal(s1, srv)

		v := r.Context().Value(k) // BaseContext 中设置了 k 的值
		a.Equal(v, 1)

		isRequested = true
	})
	go func() {
		a.Equal(srv.Serve(), http.ErrServerClosed)
	}()
	time.Sleep(500 * time.Millisecond)
	rest.NewRequest(a, nil, http.MethodGet, "http://localhost:8080/path").Do().Success()
	a.NotError(srv.Close(0))
	a.True(isRequested, "未正常访问 /path")
}

func TestServer_vars(t *testing.T) {
	a := assert.New(t)
	srv := newServer(a)

	type (
		t1 int
		t2 int64
		t3 = t2
	)
	var (
		v1 t1 = 1
		v2 t2 = 1
		v3 t3 = 1
	)

	srv.Set(v1, 1)
	srv.Set(v2, 2)
	srv.Set(v3, 3)

	v11, found := srv.Get(v1)
	a.True(found).Equal(v11, 1)
	v22, found := srv.Get(v2)
	a.True(found).Equal(v22, 3)
{'fraction_non_alphanumeric': 0.1921401802896789, 'fraction_numerical': 0.029575610250177253, 'mean_word_length': 4.375068045726729, 'pattern_counts': {'":': 4, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 8}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7519044', 'n_tokens_mistral': 4489, 'n_tokens_neox': 4223, 'n_words': 665}