blob: 1455f63b462f03bf3be6824c1be6fc5abd99ab2d [file] [log] [blame]
Tim Windelschmidt6d33a432025-02-04 14:34:25 +01001// Copyright The Monogon Project Authors.
2// SPDX-License-Identifier: Apache-2.0
3
Serge Bazanskibee272f2022-09-13 13:52:42 +02004package server
5
6import (
7 "bytes"
8 "context"
9 "encoding/binary"
10 "fmt"
11 "net/http"
12 "strconv"
13 "testing"
14
15 "google.golang.org/grpc/codes"
16 "google.golang.org/protobuf/proto"
17
18 apb "source.monogon.dev/cloud/api"
Serge Bazanskia5baa872022-09-15 18:49:35 +020019 "source.monogon.dev/cloud/apigw/model"
Serge Bazanskibee272f2022-09-13 13:52:42 +020020 "source.monogon.dev/cloud/lib/component"
21)
22
Serge Bazanskia5baa872022-09-15 18:49:35 +020023func dut() *Server {
24 return &Server{
Serge Bazanskibee272f2022-09-13 13:52:42 +020025 Config: Config{
Serge Bazanskia5baa872022-09-15 18:49:35 +020026 Component: component.ComponentConfig{
Serge Bazanskibee272f2022-09-13 13:52:42 +020027 GRPCListenAddress: ":0",
28 DevCerts: true,
29 DevCertsPath: "/tmp/foo",
30 },
Serge Bazanskia5baa872022-09-15 18:49:35 +020031 Database: component.CockroachConfig{
32 InMemory: true,
33 },
Serge Bazanskibee272f2022-09-13 13:52:42 +020034 PublicListenAddress: ":0",
35 },
36 }
Serge Bazanskia5baa872022-09-15 18:49:35 +020037}
Serge Bazanskibee272f2022-09-13 13:52:42 +020038
Serge Bazanskia5baa872022-09-15 18:49:35 +020039// TestPublicSimple ensures the public grpc-web listener is working.
40func TestPublicSimple(t *testing.T) {
41 s := dut()
Serge Bazanskibee272f2022-09-13 13:52:42 +020042 ctx := context.Background()
43 s.Start(ctx)
44
45 // Craft a gRPC-Web request from scratch. There doesn't seem to be a
46 // well-supported library to do this.
47
48 // The request is \0 ++ uint32be(len(req)) ++ req.
49 msgBytes, err := proto.Marshal(&apb.WhoAmIRequest{})
50 if err != nil {
51 t.Fatalf("Could not marshal request body: %v", err)
52 }
53 buf := bytes.NewBuffer(nil)
54 binary.Write(buf, binary.BigEndian, byte(0))
55 binary.Write(buf, binary.BigEndian, uint32(len(msgBytes)))
56 buf.Write(msgBytes)
57
58 // Perform the request. Set minimum headers required for gRPC-Web to recognize
59 // this as a gRPC-Web request.
60 req, err := http.NewRequest("POST", fmt.Sprintf("http://%s/cloud.api.IAM/WhoAmI", s.ListenPublic), buf)
61 if err != nil {
62 t.Fatalf("Could not create request: %v", err)
63 }
64 req.Header.Set("Content-Type", "application/grpc-web+proto")
65 req.Header.Set("X-Grpc-Web", "1")
66
67 res, err := http.DefaultClient.Do(req)
68 if err != nil {
69 t.Fatalf("Could not perform request: %v", err)
70 }
71 // Regardless for RPC status, 200 should always be returned.
72 if want, got := 200, res.StatusCode; want != got {
73 t.Errorf("Wanted code %d, got %d", want, got)
74 }
75
76 // Expect endpoint to return 'unimplemented'.
77 code, _ := strconv.Atoi(res.Header.Get("Grpc-Status"))
78 if want, got := uint32(codes.Unimplemented), uint32(code); want != got {
79 t.Errorf("Wanted code %d, got %d", want, got)
80 }
81 if want, got := "unimplemented", res.Header.Get("Grpc-Message"); want != got {
82 t.Errorf("Wanted message %q, got %q", want, got)
83 }
84}
Serge Bazanskia5baa872022-09-15 18:49:35 +020085
86// TestUserSimple makes sure we can add and retrieve users. This is a low-level
87// test which mostly exercises the machinery to bring up a working database in
88// tests.
89func TestUserSimple(t *testing.T) {
90 s := dut()
91 ctx := context.Background()
92 s.Start(ctx)
93
94 db, err := s.Config.Database.Connect()
95 if err != nil {
96 t.Fatalf("Connecting to the database failed: %v", err)
97 }
98 q := model.New(db)
99
100 // Start out with no account by sub 'test'.
101 accounts, err := q.GetAccountByOIDC(ctx, "test")
102 if err != nil {
103 t.Fatalf("Retrieving accounts failed: %v", err)
104 }
105 if want, got := 0, len(accounts); want != got {
106 t.Fatalf("Expected no accounts at first, got %d", got)
107 }
108
109 // Create a new test account for sub 'test'.
110 _, err = q.InitializeAccountFromOIDC(ctx, model.InitializeAccountFromOIDCParams{
111 AccountOidcSub: "test",
112 AccountDisplayName: "Test User",
113 })
114 if err != nil {
115 t.Fatalf("Creating new account failed: %v", err)
116 }
117
118 // Expect this account to be available now.
119 accounts, err = q.GetAccountByOIDC(ctx, "test")
120 if err != nil {
121 t.Fatalf("Retrieving accounts failed: %v", err)
122 }
123 if want, got := 1, len(accounts); want != got {
124 t.Fatalf("Expected exactly one account after creation, got %d", got)
125 }
126 if want, got := "Test User", accounts[0].AccountDisplayName; want != got {
127 t.Fatalf("Expected to read back display name %q, got %q", want, got)
128 }
129}