summaryrefslogtreecommitdiff
path: root/internal/api/api_test.go
diff options
context:
space:
mode:
authorJeroen Wijenbergh <jeroen.wijenbergh@geant.org>2026-02-12 12:34:08 +0100
committerJeroen Wijenbergh <jeroen.wijenbergh@geant.org>2026-02-12 12:59:03 +0100
commita30ef6b27e578a4cf0a674b24f5b52b4c1516c63 (patch)
tree27c7321cbceac2a487c1ba17151711de3d438a53 /internal/api/api_test.go
parentb00ce8214479c50e137db73c77b0cc1393c5e7d4 (diff)
All: Rename packages that sound useless or clash with std
Diffstat (limited to 'internal/api/api_test.go')
-rw-r--r--internal/api/api_test.go513
1 files changed, 0 insertions, 513 deletions
diff --git a/internal/api/api_test.go b/internal/api/api_test.go
deleted file mode 100644
index e88e816..0000000
--- a/internal/api/api_test.go
+++ /dev/null
@@ -1,513 +0,0 @@
-package api
-
-import (
- "context"
- "errors"
- "fmt"
- "io"
- "net"
- "net/http"
- "net/url"
- "reflect"
- "regexp"
- "slices"
- "strings"
- "testing"
- "time"
-
- "codeberg.org/eduVPN/eduvpn-common/internal/api/profiles"
- httpw "codeberg.org/eduVPN/eduvpn-common/internal/http"
- "codeberg.org/eduVPN/eduvpn-common/internal/test"
- "codeberg.org/eduVPN/eduvpn-common/types/protocol"
- "codeberg.org/eduVPN/eduvpn-common/types/server"
- "codeberg.org/jwijenbergh/eduoauth-go/v2"
-)
-
-func tokenHandler(t *testing.T, gt []string) func(http.ResponseWriter, *http.Request) {
- return func(w http.ResponseWriter, r *http.Request) {
- if r.Method != http.MethodPost {
- t.Fatalf("invalid HTTP method for token handler: %v", r.Method)
- }
- b, err := io.ReadAll(r.Body)
- if err != nil {
- t.Fatalf("failed reading token endpoint body: %v", err)
- }
- parsed, err := url.ParseQuery(string(b))
- if err != nil {
- t.Fatalf("failed parsing query body: %v", err)
- }
- grant := parsed.Get("grant_type")
-
- if slices.Contains(gt, grant) {
- _, err = w.Write([]byte(`
-{
- "access_token": "validaccess",
- "refresh_token": "validrefresh",
- "expires_in": 3600
-}
- `))
- if err != nil {
- t.Fatalf("failed writing in token handler: %v", err)
- }
- return
- }
- t.Fatalf("grant type: %v, not allowed", grant)
- }
-}
-
-func checkAuthBearer(t *testing.T, r *http.Request) {
- authh := r.Header.Get("Authorization")
- if !strings.HasPrefix(authh, "Bearer ") {
- t.Fatalf("API call is not given with an authorization Bearer header, got: %v", authh)
- }
-}
-
-func connectHandler(t *testing.T, proto string, exp time.Time) func(http.ResponseWriter, *http.Request) {
- return func(w http.ResponseWriter, r *http.Request) {
- if r.Method != http.MethodPost {
- t.Fatalf("invalid HTTP method for connect handler: %v", r.Method)
- }
- checkAuthBearer(t, r)
- w.Header().Set("expires", exp.Format(http.TimeFormat))
- w.Header().Set("content-type", fmt.Sprintf("application/x-%s-profile", proto))
- b, err := io.ReadAll(r.Body)
- if err != nil {
- t.Fatalf("failed reading token endpoint body: %v", err)
- }
- parsed, err := url.ParseQuery(string(b))
- if err != nil {
- t.Fatalf("failed parsing query body: %v", err)
- }
- // the wireguard config we parse
- var cfg string
- if proto == "openvpn" {
- cfg = "openvpnconfig"
- } else {
- if parsed.Get("public_key") == "" {
- t.Fatalf("no public_key given")
- }
- if proto == "wireguard+tcp" {
- ptcp := parsed.Get("prefer_tcp")
- if ptcp != "yes" {
- t.Fatalf("prefer TCP is not yes: %s", ptcp)
- }
- cfg = `
-[Interface]
-[Peer]
-ProxyEndpoint = https://proxyendpoint
-`
- } else {
- cfg = "[Interface]"
- }
- }
- _, err = w.Write([]byte(cfg))
- if err != nil {
- t.Fatalf("failed writing /connect response: %v", err)
- }
- }
-}
-
-func disconnectHandler(t *testing.T) func(http.ResponseWriter, *http.Request) {
- return func(_ http.ResponseWriter, r *http.Request) {
- if r.Method != http.MethodPost {
- t.Fatalf("invalid HTTP method for disconnect handler: %v", r.Method)
- }
- checkAuthBearer(t, r)
- }
-}
-
-type TestCallback struct {
- t *testing.T
-}
-
-func (tc *TestCallback) TriggerAuth(_ context.Context, str string, _ bool) (string, error) {
- go func() {
- u, err := url.Parse(str)
- if err != nil {
- panic(err)
- }
- ru, err := url.Parse(u.Query().Get("redirect_uri"))
- if err != nil {
- panic(err)
- }
- oq := u.Query()
- q := ru.Query()
- q.Set("state", oq.Get("state"))
- q.Set("code", "fakeauthcode")
- ru.RawQuery = q.Encode()
-
- c := http.Client{}
- req, err := http.NewRequest("GET", ru.String(), nil)
- if err != nil {
- panic(err)
- }
- _, err = c.Do(req)
- if err != nil {
- panic(err)
- }
- }()
- return "", nil
-}
-func (tc *TestCallback) AuthDone(string, server.Type) {}
-func (tc *TestCallback) TokensUpdated(string, server.Type, eduoauth.Token) {}
-
-// create a API struct with allowed grant types
-func createTestAPI(t *testing.T, tok *eduoauth.Token, gt []string, hps []test.HandlerPath) (*API, *test.Server) {
- // Create a simple API client and check if the fields are created correctly
- listen, err := net.Listen("tcp", "127.0.0.1:0")
- if err != nil {
- t.Fatalf("failed to setup listener for test server: %v", err)
- }
-
- hps = append(hps, []test.HandlerPath{
- {
- Method: http.MethodGet,
- Path: "/.well-known/vpn-user-portal",
- Response: fmt.Sprintf(`
-{
- "api": {
- "http://eduvpn.org/api#3": {
- "api_endpoint": "https://%[1]s/test-api-endpoint",
- "authorization_endpoint": "https://%[1]s/test-authorization-endpoint",
- "token_endpoint": "https://%[1]s/test-token-endpoint"
- }
- },
- "v": "0.0.0"
-}
-`, listen.Addr().String()),
- },
- {
- Path: "/test-token-endpoint",
- ResponseHandler: tokenHandler(t, gt),
- },
- }...)
- // start server
- serv := test.NewServerWithHandles(hps, listen)
- servc, err := serv.Client()
- if err != nil {
- t.Fatalf("failed to setup HTTP test server client: %v", servc)
- }
-
- sd := ServerData{
- ID: "randomidentifier",
- Type: server.TypeCustom,
- BaseWK: serv.URL,
- BaseAuthWK: serv.URL,
- ProcessAuth: func(_ context.Context, in string) (string, error) {
- return in, nil
- },
- DisableAuthorize: false,
- transport: servc.Client.Transport,
- }
-
- tc := &TestCallback{t: t}
-
- a, err := NewAPI(context.Background(), "testclient", sd, tc, tok)
- if err != nil {
- t.Fatalf("failed creating API: %v", err)
- }
- return a, serv
-}
-
-func TestNewAPI(t *testing.T) {
- gts := []string{"refresh_token"}
- tok := &eduoauth.Token{
- Access: "expiredaccess",
- Refresh: "expiredrefresh",
- // tokens are expired, let's try authorizing
- ExpiredTimestamp: time.Now(),
- }
- a, srv := createTestAPI(t, tok, gts, nil)
- srv.Close()
-
- // now the tokens should be the new access tokens
- if a.oauth.Token().Access != "validaccess" {
- t.Fatalf("access token is not valid access")
- }
- if a.oauth.Token().Refresh != "validrefresh" {
- t.Fatalf("refresh token is not valid refresh")
- }
-
- gts = []string{"authorization_code"}
- tok = &eduoauth.Token{
- Access: "expiredaccess",
- Refresh: "",
- ExpiredTimestamp: time.Now(),
- }
- a, srv = createTestAPI(t, tok, gts, nil)
- srv.Close()
-
- // now the tokens should be the new access tokens
- if a.oauth.Token().Access != "validaccess" {
- t.Fatalf("access token is not valid access")
- }
- if a.oauth.Token().Refresh != "validrefresh" {
- t.Fatalf("refresh token is not valid refresh")
- }
-}
-
-func TestAPIInfo(t *testing.T) {
- // auth should not be triggered
- var gts []string
- tok := &eduoauth.Token{
- Access: "validaccess",
- Refresh: "validrefresh",
- ExpiredTimestamp: time.Now().Add(1 * time.Hour),
- }
- statErr := &httpw.StatusError{}
- cases := []struct {
- hp test.HandlerPath
- info *profiles.Info
- err any
- }{
- {
- hp: test.HandlerPath{
- Method: http.MethodGet,
- Path: "/test-api-endpoint/info",
- Response: `
-{
- "info": {
- "profile_list": [
- {
- "default_gateway": false,
- "display_name": "test profile 1",
- "profile_id": "test1",
- "profile_priority": 3,
- "vpn_proto_list": [
- "openvpn",
- "wireguard"
- ]
- }
- ]
- }
-}
-`,
- },
- info: &profiles.Info{
- Info: profiles.ListInfo{
- ProfileList: []profiles.Profile{
- {
- ID: "test1",
- DisplayName: "test profile 1",
- VPNProtoList: []string{"openvpn", "wireguard"},
- Priority: 3,
- DefaultGateway: false,
- },
- },
- },
- },
- },
- {
- hp: test.HandlerPath{
- Method: http.MethodGet,
- Path: "/test-api-endpoint/info",
- Response: `
-{
- "info": {
- "profile_list": [
- {
- "display_name": "test profile 2",
- "profile_id": "test2",
- "vpn_proto_list": [
- "wireguard"
- ]
- }
- ]
- }
-}
-`,
- },
- info: &profiles.Info{
- Info: profiles.ListInfo{
- ProfileList: []profiles.Profile{
- {
- ID: "test2",
- DisplayName: "test profile 2",
- VPNProtoList: []string{"wireguard"},
- DefaultGateway: false,
- },
- },
- },
- },
- },
- {
- hp: test.HandlerPath{
- Method: http.MethodGet,
- Path: "/test-api-endpoint/info",
- Response: "",
- ResponseCode: 404,
- },
- info: nil,
- err: &statErr,
- },
- }
-
- for _, c := range cases {
- a, srv := createTestAPI(t, tok, gts, []test.HandlerPath{c.hp})
- defer srv.Close()
- gprfs, err := a.Info(context.Background())
- // got error but the want error is nil
- if err != nil {
- if c.err == nil {
- t.Fatalf("failed profiles info: %v but want no error", err)
- }
-
- if !errors.As(err, c.err) {
- t.Fatalf("error type not equal: %T, want: %T, error string: %s", err, c.err, err.Error())
- }
- } else if c.err != nil {
- t.Fatalf("got no error but want error: %T", c.err)
- }
-
- if !reflect.DeepEqual(gprfs, c.info) {
- t.Fatalf("got info: %v, not equal to want: %v", gprfs, c.info)
- }
- }
-}
-
-func TestAPIConnect(t *testing.T) {
- // auth should not be triggered
- var gts []string
- tok := &eduoauth.Token{
- Access: "validaccess",
- Refresh: "validrefresh",
- ExpiredTimestamp: time.Now().Add(1 * time.Hour),
- }
- cases := []struct {
- hp test.HandlerPath
- cd *ConnectData
- prof profiles.Profile
- protos []protocol.Protocol
- ptcp bool
- err error
- }{
- {
- hp: test.HandlerPath{
- Method: http.MethodPost,
- Path: "/test-api-endpoint/connect",
- Response: ``,
- },
- cd: nil,
- err: ErrNoProtocols,
- },
- {
- hp: test.HandlerPath{
- Method: http.MethodPost,
- Path: "/test-api-endpoint/connect",
- Response: ``,
- },
- cd: nil,
- protos: []protocol.Protocol{protocol.Unknown},
- err: ErrUnknownProtocol,
- },
- {
- hp: test.HandlerPath{
- Method: http.MethodPost,
- Path: "/test-api-endpoint/connect",
- Response: ``,
- },
- cd: nil,
- protos: []protocol.Protocol{protocol.OpenVPN, protocol.WireGuard, protocol.Unknown},
- err: ErrUnknownProtocol,
- },
- {
- hp: test.HandlerPath{
- Method: http.MethodPost,
- Path: "/test-api-endpoint/connect",
- ResponseHandler: connectHandler(t, "openvpn", time.Date(2000, time.January, 0, 0, 0, 0, 0, time.UTC)),
- },
- cd: &ConnectData{
- Configuration: "openvpnconfig\nscript-security 0",
- Protocol: protocol.OpenVPN,
- Expires: time.Date(2000, time.January, 0, 0, 0, 0, 0, time.UTC),
- },
- protos: []protocol.Protocol{protocol.OpenVPN, protocol.WireGuard},
- err: nil,
- },
- {
- hp: test.HandlerPath{
- Method: http.MethodPost,
- Path: "/test-api-endpoint/connect",
- ResponseHandler: connectHandler(t, "wireguard", time.Date(2000, time.January, 0, 0, 0, 0, 0, time.UTC)),
- },
- cd: &ConnectData{
- Configuration: `\[Interface\]
-PrivateKey = .*`,
- Protocol: protocol.WireGuard,
- Expires: time.Date(2000, time.January, 0, 0, 0, 0, 0, time.UTC),
- },
- protos: []protocol.Protocol{protocol.OpenVPN, protocol.WireGuard},
- err: nil,
- },
- {
- hp: test.HandlerPath{
- Method: http.MethodPost,
- Path: "/test-api-endpoint/connect",
- ResponseHandler: connectHandler(t, "wireguard+tcp", time.Date(2000, time.January, 0, 0, 0, 0, 0, time.UTC)),
- },
- cd: &ConnectData{
- Configuration: `\[Interface\]
-PrivateKey = .*`,
- Protocol: protocol.WireGuardProxy,
- Expires: time.Date(2000, time.January, 0, 0, 0, 0, 0, time.UTC),
- },
- ptcp: true,
- protos: []protocol.Protocol{protocol.OpenVPN, protocol.WireGuard},
- err: nil,
- },
- }
-
- for _, c := range cases {
- a, srv := createTestAPI(t, tok, gts, []test.HandlerPath{c.hp})
- defer srv.Close()
- gcd, err := a.Connect(context.Background(), c.prof, c.protos, c.ptcp)
- // got error but the want error is nil
- if err != nil {
- if c.err == nil {
- t.Fatalf("failed connect: %v but want no error", err)
- }
-
- if !errors.Is(err, c.err) {
- t.Fatalf("error type not equal: %T, want: %T, error string: %s", err, c.err, err)
- }
- } else if c.err != nil {
- t.Fatalf("got no error but want error: %T", c.err)
- }
-
- if gcd != nil && c.cd != nil {
- m, err := regexp.MatchString(c.cd.Configuration, gcd.Configuration)
- if err != nil {
- t.Fatalf("failed matching regexp: %v", err)
- }
- if !m {
- t.Fatalf("regex:\n%s\ndoes not match config:\n%s", c.cd.Configuration, gcd.Configuration)
- }
- // we have already checked the config using a regex
- c.cd.Configuration = gcd.Configuration
-
- }
- if !reflect.DeepEqual(gcd, c.cd) {
- t.Fatalf("got connect data: %v, not equal to want: %v", gcd, c.cd)
- }
- }
-}
-
-func TestDisconnect(t *testing.T) {
- var gts []string
- tok := &eduoauth.Token{
- Access: "validaccess",
- Refresh: "validrefresh",
- ExpiredTimestamp: time.Now().Add(1 * time.Hour),
- }
- a, srv := createTestAPI(t, tok, gts, []test.HandlerPath{
- {
- Path: "/test-api-endpoint/disconnect",
- ResponseHandler: disconnectHandler(t),
- },
- })
- defer srv.Close()
- err := a.Disconnect(context.Background())
- if err != nil {
- t.Fatalf("failed /disconnect: %v", err)
- }
-}