mirror of
https://github.com/hanzoai/tasks.git
synced 2026-10-04 02:04:03 +00:00
## What changed? Inlined the server and client code from the Nexus SDK ## Why? The protocol has been deemed to stay experimental for a while longer and we want to get trim Nexus Go SDK to the bare minimum. ## How did you test it? - [x] built - [x] run locally and tested manually - [x] covered by existing tests - [x] added new unit test(s)
464 lines
11 KiB
Go
464 lines
11 KiB
Go
package nexusrpc
|
|
|
|
import (
|
|
"net/http"
|
|
"net/url"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/nexus-rpc/sdk-go/nexus"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestAddLinksToHeader(t *testing.T) {
|
|
type testcase struct {
|
|
name string
|
|
input []nexus.Link
|
|
output http.Header
|
|
errMsg string
|
|
}
|
|
|
|
cases := []testcase{
|
|
{
|
|
name: "single link",
|
|
input: []nexus.Link{{
|
|
URL: &url.URL{
|
|
Scheme: "https",
|
|
Host: "example.com",
|
|
Path: "/path/to/something",
|
|
RawQuery: "param=value",
|
|
},
|
|
Type: "url",
|
|
}},
|
|
output: http.Header{
|
|
http.CanonicalHeaderKey(headerLink): []string{
|
|
`<https://example.com/path/to/something?param=value>; type="url"`,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "multiple links",
|
|
input: []nexus.Link{
|
|
{
|
|
URL: &url.URL{
|
|
Scheme: "https",
|
|
Host: "example.com",
|
|
Path: "/path/to/something",
|
|
RawQuery: "param=value",
|
|
},
|
|
Type: "url",
|
|
},
|
|
{
|
|
URL: &url.URL{
|
|
Scheme: "https",
|
|
Host: "foo.com",
|
|
Path: "/path/to/something",
|
|
RawQuery: "bar=value",
|
|
},
|
|
Type: "url",
|
|
},
|
|
},
|
|
output: http.Header{
|
|
http.CanonicalHeaderKey(headerLink): []string{
|
|
`<https://example.com/path/to/something?param=value>; type="url"`,
|
|
`<https://foo.com/path/to/something?bar=value>; type="url"`,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "invalid link",
|
|
input: []nexus.Link{{
|
|
URL: &url.URL{
|
|
Scheme: "https",
|
|
Host: "example.com",
|
|
Path: "/path/to/something",
|
|
RawQuery: "param=value%",
|
|
},
|
|
Type: "url",
|
|
}},
|
|
errMsg: "failed to encode link",
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
output := http.Header{}
|
|
err := addLinksToHTTPHeader(tc.input, output)
|
|
if tc.errMsg != "" {
|
|
require.ErrorContains(t, err, tc.errMsg)
|
|
} else {
|
|
require.NoError(t, err)
|
|
require.Equal(t, tc.output, output)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetLinksFromHeader(t *testing.T) {
|
|
type testcase struct {
|
|
name string
|
|
input http.Header
|
|
output []nexus.Link
|
|
errMsg string
|
|
}
|
|
|
|
cases := []testcase{
|
|
{
|
|
name: "single link",
|
|
input: http.Header{
|
|
http.CanonicalHeaderKey(headerLink): []string{
|
|
`<https://example.com/path/to/something?param=value>; type="url"`,
|
|
},
|
|
},
|
|
output: []nexus.Link{{
|
|
URL: &url.URL{
|
|
Scheme: "https",
|
|
Host: "example.com",
|
|
Path: "/path/to/something",
|
|
RawQuery: "param=value",
|
|
},
|
|
Type: "url",
|
|
}},
|
|
},
|
|
{
|
|
name: "multiple links",
|
|
input: http.Header{
|
|
http.CanonicalHeaderKey(headerLink): []string{
|
|
`<https://example.com/path/to/something?param=value>; type="url"`,
|
|
`<https://foo.com/path/to/something?bar=value>; type="url"`,
|
|
},
|
|
},
|
|
output: []nexus.Link{
|
|
{
|
|
URL: &url.URL{
|
|
Scheme: "https",
|
|
Host: "example.com",
|
|
Path: "/path/to/something",
|
|
RawQuery: "param=value",
|
|
},
|
|
Type: "url",
|
|
},
|
|
{
|
|
URL: &url.URL{
|
|
Scheme: "https",
|
|
Host: "foo.com",
|
|
Path: "/path/to/something",
|
|
RawQuery: "bar=value",
|
|
},
|
|
Type: "url",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "multiple links single header",
|
|
input: http.Header{
|
|
http.CanonicalHeaderKey(headerLink): []string{
|
|
`<https://example.com/path/to/something?param=value>; type="url", <https://foo.com/path/to/something?bar=value>; type="url"`,
|
|
},
|
|
},
|
|
output: []nexus.Link{
|
|
{
|
|
URL: &url.URL{
|
|
Scheme: "https",
|
|
Host: "example.com",
|
|
Path: "/path/to/something",
|
|
RawQuery: "param=value",
|
|
},
|
|
Type: "url",
|
|
},
|
|
{
|
|
URL: &url.URL{
|
|
Scheme: "https",
|
|
Host: "foo.com",
|
|
Path: "/path/to/something",
|
|
RawQuery: "bar=value",
|
|
},
|
|
Type: "url",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "invalid header",
|
|
input: http.Header{
|
|
http.CanonicalHeaderKey(headerLink): []string{
|
|
`<https://example.com/path?param=value> type="url"`,
|
|
},
|
|
},
|
|
errMsg: "failed to parse link header",
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
output, err := getLinksFromHeader(tc.input)
|
|
if tc.errMsg != "" {
|
|
require.ErrorContains(t, err, tc.errMsg)
|
|
} else {
|
|
require.NoError(t, err)
|
|
require.Equal(t, tc.output, output)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestEncodeLink(t *testing.T) {
|
|
type testcase struct {
|
|
name string
|
|
input nexus.Link
|
|
output string
|
|
errMsg string
|
|
}
|
|
|
|
cases := []testcase{
|
|
{
|
|
name: "valid",
|
|
input: nexus.Link{
|
|
URL: &url.URL{
|
|
Scheme: "https",
|
|
Host: "example.com",
|
|
Path: "/path/to/something",
|
|
RawQuery: "param1=value1¶m2=value2",
|
|
},
|
|
Type: "text/plain",
|
|
},
|
|
output: `<https://example.com/path/to/something?param1=value1¶m2=value2>; type="text/plain"`,
|
|
},
|
|
{
|
|
name: "valid custom URL",
|
|
input: nexus.Link{
|
|
URL: &url.URL{
|
|
Scheme: "nexus",
|
|
Path: "/path/to/something",
|
|
RawQuery: "param1=value1",
|
|
},
|
|
Type: "nexus.data_type",
|
|
},
|
|
output: `<nexus:///path/to/something?param1=value1>; type="nexus.data_type"`,
|
|
},
|
|
{
|
|
name: "invalid url empty",
|
|
input: nexus.Link{
|
|
URL: &url.URL{},
|
|
Type: "text/plain",
|
|
},
|
|
errMsg: "failed to encode link",
|
|
},
|
|
{
|
|
name: "invalid query not percent-encoded",
|
|
input: nexus.Link{
|
|
URL: &url.URL{
|
|
Scheme: "https",
|
|
Host: "example.com",
|
|
Path: "/path/to/something",
|
|
RawQuery: "param1=value1¶m2=value2;",
|
|
},
|
|
Type: "text/plain",
|
|
},
|
|
errMsg: "failed to encode link",
|
|
},
|
|
{
|
|
name: "invalid type empty",
|
|
input: nexus.Link{
|
|
URL: &url.URL{
|
|
Scheme: "https",
|
|
Host: "example.com",
|
|
Path: "/path/to/something",
|
|
RawQuery: "param1=value1¶m2=value2",
|
|
},
|
|
Type: "",
|
|
},
|
|
errMsg: "failed to encode link",
|
|
},
|
|
{
|
|
name: "invalid type invalid chars",
|
|
input: nexus.Link{
|
|
URL: &url.URL{
|
|
Scheme: "https",
|
|
Host: "example.com",
|
|
Path: "/path/to/something",
|
|
RawQuery: "param1=value1¶m2=value2",
|
|
},
|
|
Type: "text/plain;",
|
|
},
|
|
errMsg: "failed to encode link",
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
output, err := encodeLink(tc.input)
|
|
if tc.errMsg != "" {
|
|
require.ErrorContains(t, err, tc.errMsg)
|
|
} else {
|
|
require.NoError(t, err)
|
|
require.Equal(t, tc.output, output)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDecodeLink(t *testing.T) {
|
|
type testcase struct {
|
|
name string
|
|
input string
|
|
output nexus.Link
|
|
errMsg string
|
|
}
|
|
|
|
cases := []testcase{
|
|
{
|
|
name: "valid",
|
|
input: `<https://example.com/path/to/something?param1=value1¶m2=value2>; type="text/plain"`,
|
|
output: nexus.Link{
|
|
URL: &url.URL{
|
|
Scheme: "https",
|
|
Host: "example.com",
|
|
Path: "/path/to/something",
|
|
RawQuery: "param1=value1¶m2=value2",
|
|
},
|
|
Type: "text/plain",
|
|
},
|
|
},
|
|
{
|
|
name: "valid multiple params",
|
|
input: `<https://example.com/path/to/something?param1=value1¶m2=value2>; type="text/plain"; Param="value"`,
|
|
output: nexus.Link{
|
|
URL: &url.URL{
|
|
Scheme: "https",
|
|
Host: "example.com",
|
|
Path: "/path/to/something",
|
|
RawQuery: "param1=value1¶m2=value2",
|
|
},
|
|
Type: "text/plain",
|
|
},
|
|
},
|
|
{
|
|
name: "valid param not quoted",
|
|
input: `<https://example.com/path/to/something?param1=value1¶m2=value2>; type=text/plain`,
|
|
output: nexus.Link{
|
|
URL: &url.URL{
|
|
Scheme: "https",
|
|
Host: "example.com",
|
|
Path: "/path/to/something",
|
|
RawQuery: "param1=value1¶m2=value2",
|
|
},
|
|
Type: "text/plain",
|
|
},
|
|
},
|
|
{
|
|
name: "valid custom URL",
|
|
input: `<nexus:///path/to/something?param=value>; type="nexus.data_type"`,
|
|
output: nexus.Link{
|
|
URL: &url.URL{
|
|
Scheme: "nexus",
|
|
Path: "/path/to/something",
|
|
RawQuery: "param=value",
|
|
},
|
|
Type: "nexus.data_type",
|
|
},
|
|
},
|
|
{
|
|
name: "invalid url",
|
|
input: `<https://example.com/path/to/something%?param1=value1¶m2=value2>`,
|
|
errMsg: "failed to parse link header",
|
|
},
|
|
{
|
|
name: "invalid trailing semi-colon",
|
|
input: `<https://example.com/path/to/something?param1=value1¶m2=value2>; type="text/plain";`,
|
|
errMsg: "failed to parse link header",
|
|
},
|
|
{
|
|
name: "invalid empty param part",
|
|
input: `<https://example.com/path/to/something?param1=value1¶m2=value2>; ; type="text/plain`,
|
|
errMsg: "failed to parse link header",
|
|
},
|
|
{
|
|
name: "invalid no type param trailing semi-colon",
|
|
input: `<https://example.com/path/to/something?param1=value1¶m2=value2>;`,
|
|
errMsg: "failed to parse link header",
|
|
},
|
|
{
|
|
name: "invalid url not enclosed",
|
|
input: `https://example.com/path/to/something?param1=value1¶m2=value2; type="text/plain"`,
|
|
errMsg: "failed to parse link header",
|
|
},
|
|
{
|
|
name: "invalid url missing closing bracket",
|
|
input: `<https://example.com/path/to/something?param1=value1¶m2=value2; type="text/plain"`,
|
|
errMsg: "failed to parse link header",
|
|
},
|
|
{
|
|
name: "invalid url missing opening bracket",
|
|
input: `https://example.com/path/to/something?param1=value1¶m2=value2>; type="text/plain"`,
|
|
errMsg: "failed to parse link header",
|
|
},
|
|
{
|
|
name: "invalid param missing quote",
|
|
input: `https://example.com/path/to/something?param1=value1¶m2=value2>; type="text/plain`,
|
|
errMsg: "failed to parse link header",
|
|
},
|
|
{
|
|
name: "invalid multiple params missing semi-colon",
|
|
input: `https://example.com/path/to/something?param1=value1¶m2=value2>; type="text/plain" Param=value`,
|
|
errMsg: "failed to parse link header",
|
|
},
|
|
{
|
|
name: "invalid missing semi-colon after url",
|
|
input: `https://example.com/path/to/something?param1=value1¶m2=value2> type="text/plain"`,
|
|
errMsg: "failed to parse link header",
|
|
},
|
|
{
|
|
name: "invalid param missing value",
|
|
input: `https://example.com/path/to/something?param1=value1¶m2=value2>; type`,
|
|
errMsg: "failed to parse link header",
|
|
},
|
|
{
|
|
name: "invalid param missing value with equal sign",
|
|
input: `<https://example.com/path/to/something?param1=value1¶m2=value2>; type=`,
|
|
errMsg: "failed to parse link header",
|
|
},
|
|
{
|
|
name: "invalid missing type key",
|
|
input: `<https://example.com/path/to/something?param1=value1¶m2=value2>`,
|
|
errMsg: "failed to parse link header",
|
|
},
|
|
{
|
|
name: "invalid url empty",
|
|
input: `<>; type="text/plain"`,
|
|
errMsg: "failed to parse link header",
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
output, err := decodeLink(tc.input)
|
|
if tc.errMsg != "" {
|
|
require.ErrorContains(t, err, tc.errMsg)
|
|
} else {
|
|
require.NoError(t, err)
|
|
require.Equal(t, tc.output, output)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestParseDuration(t *testing.T) {
|
|
_, err := ParseDuration("invalid")
|
|
require.ErrorContains(t, err, "invalid duration:")
|
|
d, err := ParseDuration("10ms")
|
|
require.NoError(t, err)
|
|
require.Equal(t, 10*time.Millisecond, d)
|
|
d, err = ParseDuration("10.1ms")
|
|
require.NoError(t, err)
|
|
require.Equal(t, 10*time.Millisecond, d)
|
|
d, err = ParseDuration("1s")
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1*time.Second, d)
|
|
d, err = ParseDuration("999m")
|
|
require.NoError(t, err)
|
|
require.Equal(t, 999*time.Minute, d)
|
|
d, err = ParseDuration("1.3s")
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1300*time.Millisecond, d)
|
|
}
|