| package server | 
 |  | 
 | import ( | 
 | 	"bytes" | 
 | 	"context" | 
 | 	"encoding/binary" | 
 | 	"fmt" | 
 | 	"net/http" | 
 | 	"strconv" | 
 | 	"testing" | 
 |  | 
 | 	"google.golang.org/grpc/codes" | 
 | 	"google.golang.org/protobuf/proto" | 
 |  | 
 | 	apb "source.monogon.dev/cloud/api" | 
 | 	"source.monogon.dev/cloud/apigw/model" | 
 | 	"source.monogon.dev/cloud/lib/component" | 
 | ) | 
 |  | 
 | func dut() *Server { | 
 | 	return &Server{ | 
 | 		Config: Config{ | 
 | 			Component: component.ComponentConfig{ | 
 | 				GRPCListenAddress: ":0", | 
 | 				DevCerts:          true, | 
 | 				DevCertsPath:      "/tmp/foo", | 
 | 			}, | 
 | 			Database: component.CockroachConfig{ | 
 | 				InMemory: true, | 
 | 			}, | 
 | 			PublicListenAddress: ":0", | 
 | 		}, | 
 | 	} | 
 | } | 
 |  | 
 | // TestPublicSimple ensures the public grpc-web listener is working. | 
 | func TestPublicSimple(t *testing.T) { | 
 | 	s := dut() | 
 | 	ctx := context.Background() | 
 | 	s.Start(ctx) | 
 |  | 
 | 	// Craft a gRPC-Web request from scratch. There doesn't seem to be a | 
 | 	// well-supported library to do this. | 
 |  | 
 | 	// The request is \0 ++ uint32be(len(req)) ++ req. | 
 | 	msgBytes, err := proto.Marshal(&apb.WhoAmIRequest{}) | 
 | 	if err != nil { | 
 | 		t.Fatalf("Could not marshal request body: %v", err) | 
 | 	} | 
 | 	buf := bytes.NewBuffer(nil) | 
 | 	binary.Write(buf, binary.BigEndian, byte(0)) | 
 | 	binary.Write(buf, binary.BigEndian, uint32(len(msgBytes))) | 
 | 	buf.Write(msgBytes) | 
 |  | 
 | 	// Perform the request. Set minimum headers required for gRPC-Web to recognize | 
 | 	// this as a gRPC-Web request. | 
 | 	req, err := http.NewRequest("POST", fmt.Sprintf("http://%s/cloud.api.IAM/WhoAmI", s.ListenPublic), buf) | 
 | 	if err != nil { | 
 | 		t.Fatalf("Could not create request: %v", err) | 
 | 	} | 
 | 	req.Header.Set("Content-Type", "application/grpc-web+proto") | 
 | 	req.Header.Set("X-Grpc-Web", "1") | 
 |  | 
 | 	res, err := http.DefaultClient.Do(req) | 
 | 	if err != nil { | 
 | 		t.Fatalf("Could not perform request: %v", err) | 
 | 	} | 
 | 	// Regardless for RPC status, 200 should always be returned. | 
 | 	if want, got := 200, res.StatusCode; want != got { | 
 | 		t.Errorf("Wanted code %d, got %d", want, got) | 
 | 	} | 
 |  | 
 | 	// Expect endpoint to return 'unimplemented'. | 
 | 	code, _ := strconv.Atoi(res.Header.Get("Grpc-Status")) | 
 | 	if want, got := uint32(codes.Unimplemented), uint32(code); want != got { | 
 | 		t.Errorf("Wanted code %d, got %d", want, got) | 
 | 	} | 
 | 	if want, got := "unimplemented", res.Header.Get("Grpc-Message"); want != got { | 
 | 		t.Errorf("Wanted message %q, got %q", want, got) | 
 | 	} | 
 | } | 
 |  | 
 | // TestUserSimple makes sure we can add and retrieve users. This is a low-level | 
 | // test which mostly exercises the machinery to bring up a working database in | 
 | // tests. | 
 | func TestUserSimple(t *testing.T) { | 
 | 	s := dut() | 
 | 	ctx := context.Background() | 
 | 	s.Start(ctx) | 
 |  | 
 | 	db, err := s.Config.Database.Connect() | 
 | 	if err != nil { | 
 | 		t.Fatalf("Connecting to the database failed: %v", err) | 
 | 	} | 
 | 	q := model.New(db) | 
 |  | 
 | 	// Start out with no account by sub 'test'. | 
 | 	accounts, err := q.GetAccountByOIDC(ctx, "test") | 
 | 	if err != nil { | 
 | 		t.Fatalf("Retrieving accounts failed: %v", err) | 
 | 	} | 
 | 	if want, got := 0, len(accounts); want != got { | 
 | 		t.Fatalf("Expected no accounts at first, got %d", got) | 
 | 	} | 
 |  | 
 | 	// Create a new test account for sub 'test'. | 
 | 	_, err = q.InitializeAccountFromOIDC(ctx, model.InitializeAccountFromOIDCParams{ | 
 | 		AccountOidcSub:     "test", | 
 | 		AccountDisplayName: "Test User", | 
 | 	}) | 
 | 	if err != nil { | 
 | 		t.Fatalf("Creating new account failed: %v", err) | 
 | 	} | 
 |  | 
 | 	// Expect this account to be available now. | 
 | 	accounts, err = q.GetAccountByOIDC(ctx, "test") | 
 | 	if err != nil { | 
 | 		t.Fatalf("Retrieving accounts failed: %v", err) | 
 | 	} | 
 | 	if want, got := 1, len(accounts); want != got { | 
 | 		t.Fatalf("Expected exactly one account after creation, got %d", got) | 
 | 	} | 
 | 	if want, got := "Test User", accounts[0].AccountDisplayName; want != got { | 
 | 		t.Fatalf("Expected to read back display name %q, got %q", want, got) | 
 | 	} | 
 | } |