You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

294 lines
6.1 KiB

5 months ago
package servertiming
import (
"net/http"
"reflect"
"testing"
"time"
)
// Example taken from the W3C spec
// see: https://w3c.github.io/server-timing/#examples
//
// ```
// > GET /resource HTTP/1.1
// > Host: example.com
//
//
// < HTTP/1.1 200 OK
// < Server-Timing: miss, db;dur=53, app;dur=47.2
// < Server-Timing: customView, dc;desc=atl
// < Server-Timing: cache;desc="Cache Read";dur=23.2
// < Trailer: Server-Timing
// < (... snip response body ...)
// < Server-Timing: total;dur=123.4
// ```
//
// | Name | Duration | Description |
// | ---------- | -------- | ----------- |
// | miss | | |
// | db | 53 | |
// | app | 47.2 | |
// | customView | | |
// | dc | | atl |
// | cache | 23.2 | Cache Read |
// | total | 123.4 | |
func TestServerTiming_String(t *testing.T) {
tests := []struct {
name string
st ServerTiming
want string
}{
{
name: "just name",
st: ServerTiming{Name: "miss"},
want: "miss",
},
{
name: "name and dur",
st: ServerTiming{
Name: "db",
Dur: 53 * time.Millisecond,
},
want: "db;dur=53",
},
{
name: "name and decimal dur",
st: ServerTiming{
Name: "app",
Dur: 47_200 * time.Microsecond,
DecimalPrecision: 1,
},
want: "app;dur=47.2",
},
{
name: "name and nanosecond dur",
st: ServerTiming{
Name: "app",
Dur: 47_200 * time.Microsecond,
DecimalPrecision: 6,
},
want: "app;dur=47.200000",
},
{
name: "name and dur, negative precision",
st: ServerTiming{
Name: "app",
Dur: 47_200 * time.Microsecond,
DecimalPrecision: -1,
},
want: "app;dur=47",
},
{
name: "name and dur, out-of-bound precision",
st: ServerTiming{
Name: "app",
Dur: 47_200 * time.Microsecond,
DecimalPrecision: 7,
},
want: "app;dur=47.200000",
},
{
name: "name and desc",
st: ServerTiming{
Name: "dc",
Desc: "atl",
},
want: "dc;desc=atl",
},
{
name: "name, desc, and dur",
st: ServerTiming{
Name: "cache",
Dur: 23 * time.Millisecond,
Desc: "Cache Read",
},
want: "cache;dur=23;desc=Cache Read",
},
{
name: "name, desc, and decimal dur",
st: ServerTiming{
Name: "cache",
Dur: 23_200 * time.Microsecond,
Desc: "Cache Read",
DecimalPrecision: 1,
},
want: "cache;dur=23.2;desc=Cache Read",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := tt.st.String(); got != tt.want {
t.Errorf("ServerTiming.String() = %v, want %v", got, tt.want)
}
})
}
}
func TestFromString(t *testing.T) {
tests := []struct {
name string
s string
want ServerTiming
}{
{
name: "empty",
s: "",
want: ServerTiming{},
},
{
name: "name only",
s: "miss",
want: ServerTiming{Name: "miss"},
},
{
name: "name and dur",
s: "db;dur=53",
want: ServerTiming{
Name: "db",
Dur: 53 * time.Millisecond,
},
},
{
name: "name, dur and desc",
s: "cache;dur=23;desc=Cache Read",
want: ServerTiming{
Name: "cache",
Dur: 23 * time.Millisecond,
Desc: "Cache Read",
},
},
{
name: "name, desc",
s: "cache;desc=Cache Read;dur=23",
want: ServerTiming{
Name: "cache",
Dur: 23 * time.Millisecond,
Desc: "Cache Read",
},
},
{
name: "name, dur and desc with padding",
s: "cache ; dur=23 ; desc=Cache Read ",
want: ServerTiming{
Name: "cache",
Dur: 23 * time.Millisecond,
Desc: "Cache Read",
},
},
{
name: "name and decimal dur",
s: "cache;dur=23.2",
want: ServerTiming{
Name: "cache",
Dur: 23_200 * time.Microsecond,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := FromString(tt.s); !reflect.DeepEqual(got, tt.want) {
t.Errorf("FromString() = %v, want %v", got, tt.want)
}
})
}
}
func TestAppend(t *testing.T) {
resp := &http.Response{
Header: http.Header{},
}
tests := []struct {
name string
st ServerTiming
}{
{
name: "name only",
st: ServerTiming{Name: "miss"},
},
{
name: "name and dur",
st: ServerTiming{
Name: "db",
Dur: 53 * time.Millisecond,
},
},
{
name: "name, dur and desc",
st: ServerTiming{
Name: "cache",
Dur: 23 * time.Millisecond,
Desc: "Cache Read",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Append(resp, tt.st)
})
}
vals := resp.Header.Values("Server-Timing")
if len(vals) != len(tests) {
t.Errorf("Expected %d values in the headers, got %d", len(tests), len(vals))
}
for i, v := range vals {
if v != tests[i].st.String() {
t.Errorf("Expected '%s', got %s", tests[i].st.String(), v)
}
}
}
func TestTrailer(t *testing.T) {
type args struct {
r *http.Response
t ServerTiming
}
tests := []struct {
name string
args args
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Trailer(tt.args.r, tt.args.t)
})
}
}
func TestParse(t *testing.T) {
resp := &http.Response{
Header: http.Header{},
Trailer: http.Header{},
}
resp.Header.Add("Server-Timing", "miss, db;dur=53, app;dur=47")
resp.Header.Add("Server-Timing", "customView, dc;desc=atl")
resp.Header.Add("Server-Timing", `cache;desc="Cache Read";dur=23`)
resp.Trailer.Add("Server-Timing", "total;dur=123")
timings := Parse(resp)
if len(timings) != 7 {
t.Errorf("Expected 7 timings, got %d", len(timings))
}
}
func TestParseWithDecimal(t *testing.T) {
resp := &http.Response{
Header: http.Header{},
Trailer: http.Header{},
}
resp.Header.Add("Server-Timing", "miss, db;dur=53, app;dur=47.2")
resp.Header.Add("Server-Timing", "customView, dc;desc=atl")
resp.Header.Add("Server-Timing", `cache;desc="Cache Read";dur=23.2`)
resp.Trailer.Add("Server-Timing", "total;dur=123.4")
timings := Parse(resp)
if len(timings) != 7 {
t.Errorf("Expected 7 timings, got %d", len(timings))
}
}