Repository: google/exposure-notifications-server
Pull request #1474 (2021-03-19 15:51:38)

Add more tests for admin routes and template
**Release Note**

<!-- Enter your extended release note in the below block. If the PR requires
additional action from users switching to the new release, include the string
"action required". If no release note is required, write "NONE". -->

```release-note
Add more tests for admin routes and template
```


Patch:
diff --git a/internal/admin/template.go b/internal/admin/template.go
index 8e7f5b0..0ff2be0 100644
--- a/internal/admin/template.go
+++ b/internal/admin/template.go
@@ -55,6 +55,8 @@ func timestampFormatter(f string) func(i interface{}) (string, error) {
 // deref dereferences a pointer into its concrete type.
 func deref(i interface{}) (string, error) {
 	switch t := i.(type) {
+	case nil:
+		return "", nil
 	case *string:
 		if t == nil {
 			return "", nil
diff --git a/internal/admin/util.go b/internal/admin/util.go
index 88d1cff..0c820a0 100644
--- a/internal/admin/util.go
+++ b/internal/admin/util.go
@@ -20,10 +20,6 @@ func (t TemplateMap) AddTitle(title string) {
 	t["title"] = title
 }
 
-func (t TemplateMap) AddSubNav(name string) {
-	t["subnav"] = name
-}
-
 func (t TemplateMap) AddErrors(errors ...string) {
 	t["error"] = errors
 }


Test patch:
diff --git a/internal/admin/admin_test.go b/internal/admin/admin_test.go
index e4a9f07..ecca566 100644
--- a/internal/admin/admin_test.go
+++ b/internal/admin/admin_test.go
@@ -26,6 +26,7 @@ import (
 	"runtime"
 	"strings"
 	"testing"
+	"time"
 
 	"github.com/gin-gonic/gin"
 	"github.com/google/exposure-notifications-server/internal/project"
@@ -171,9 +172,79 @@ func intPtr(i int) *int {
 	return &i
 }
 
+func int8Ptr(i int8) *int8 {
+	if i == 0 {
+		return nil
+	}
+	return &i
+}
+
+func int16Ptr(i int16) *int16 {
+	if i == 0 {
+		return nil
+	}
+	return &i
+}
+
+func int32Ptr(i int32) *int32 {
+	if i == 0 {
+		return nil
+	}
+	return &i
+}
+
+func int64Ptr(i int64) *int64 {
+	if i == 0 {
+		return nil
+	}
+	return &i
+}
+
+func uintPtr(i uint) *uint {
+	if i == 0 {
+		return nil
+	}
+	return &i
+}
+
+func uint8Ptr(i uint8) *uint8 {
+	if i == 0 {
+		return nil
+	}
+	return &i
+}
+
+func uint16Ptr(i uint16) *uint16 {
+	if i == 0 {
+		return nil
+	}
+	return &i
+}
+
+func uint32Ptr(i uint32) *uint32 {
+	if i == 0 {
+		return nil
+	}
+	return &i
+}
+
+func uint64Ptr(i uint64) *uint64 {
+	if i == 0 {
+		return nil
+	}
+	return &i
+}
+
 func stringPtr(s string) *string {
 	if s == "" {
 		return nil
 	}
 	return &s
 }
+
+func timePtr(t time.Time) *time.Time {
+	if t.IsZero() {
+		return nil
+	}
+	return &t
+}
diff --git a/internal/admin/server_test.go b/internal/admin/server_test.go
new file mode 100644
index 0000000..3e59b6c
--- /dev/null
+++ b/internal/admin/server_test.go
@@ -0,0 +1,67 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, softwar
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package admin
+
+import (
+	"net/http"
+	"net/http/httptest"
+	"testing"
+
+	"github.com/google/exposure-notifications-server/internal/project"
+)
+
+func TestServerRoutes(t *testing.T) {
+	t.Parallel()
+
+	ctx := project.TestContext(t)
+
+	_, s := newTestServer(t)
+	h := s.Routes(ctx)
+
+	cases := []struct {
+		name string
+		path string
+	}{
+		{"assets", "/assets/styles.css"},
+		{"index", "/"},
+		{"apps", "/app"},
+		{"health_authority", "/healthauthority/0"},
+		{"exports", "/exports/0"},
+		{"export_importers", "/export-importers/0"},
+		{"mirrors", "/mirrors/0"},
+		{"siginfo", "/siginfo/0"},
+		{"health", "/health"},
+	}
+
+	for _, tc := range cases {
+		tc := tc
+
+		t.Run(tc.name, func(t *testing.T) {
+			t.Parallel()
+
+			r, err := http.NewRequestWithContext(ctx, http.MethodGet, tc.path, nil)
+			if err != nil {
+				t.Fatal(err)
+			}
+			w := httptest.NewRecorder()
+			h.ServeHTTP(w, r)
+			w.Flush()
+
+			if got, want := w.Code, 200; got != want {
+				t.Errorf("expected status %d to be %d; headers: %#v; body: %s", got, want, w.Header(), w.Body.String())
+			}
+		})
+	}
+}
diff --git a/internal/admin/template_test.go b/internal/admin/template_test.go
new file mode 100644
index 0000000..e8cc8cd
--- /dev/null
+++ b/internal/admin/template_test.go
@@ -0,0 +1,240 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, softwar
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package admin
+
+import (
+	"testing"
+	"time"
+)
+
+func TestTimestampFormatter(t *testing.T) {
+	t.Parallel()
+
+	formatter := timestampFormatter("2006-01-02")
+
+	cases := []struct {
+		name string
+		in   interface{}
+		exp  string
+		err  bool
+	}{
+		{
+			name: "nil",
+			in:   nil,
+			exp:  "",
+		},
+		{
+			name: "time_zero",
+			in:   time.Time{},
+			exp:  "",
+		},
+		{
+			name: "time_non_zero",
+			in:   time.Unix(1000000000, 0).UTC(),
+			exp:  "2001-09-09",
+		},
+		{
+			name: "time_ptr_nil",
+			in:   (*time.Time)(nil),
+			exp:  "",
+		},
+		{
+			name: "time_ptr_zero",
+			in:   &time.Time{},
+			exp:  "",
+		},
+		{
+			name: "time_ptr_non_zero",
+			in:   timePtr(time.Unix(1000000000, 0).UTC()),
+			exp:  "2001-09-09",
+		},
+		{
+			name: "string",
+			in:   "foo",
+			exp:  "foo",
+		},
+		{
+			name: "other",
+			in:   64,
+			err:  true,
+		},
+	}
+
+	for _, tc := range cases {
+		tc := tc
+
+		t.Run(tc.name, func(t *testing.T) {
+			t.Parallel()
+
+			got, err := formatter(tc.in)
+			if (err != nil) != tc.err {
+				t.Fatal(err)
+			}
+
+			if got, want := got, tc.exp; got != want {
+				t.Errorf("expected %q to be %q", got, want)
+			}
+		})
+	}
+}
+
+func TestDeref(t *testing.T) {
+	t.Parallel()
+
+	cases := []struct {
+		name string
+		in   interface{}
+		exp  string
+		err  bool
+	}{
+		{
+			name: "nil",
+			in:   nil,
+			exp:  "",
+		},
+		{
+			name: "unknown",
+			in:   &struct{}{},
+			err:  true,
+		},
+		{
+			name: "string_ptr",
+			in:   stringPtr("hi"),
+			exp:  "hi",
+		},
+		{
+			name: "string_ptr_nil",
+			in:   (*string)(nil),
+			exp:  "",
+		},
+		{
+			name: "int_ptr",
+			in:   intPtr(123),
+			exp:  "123",
+		},
+		{
+			name: "int_ptr_nil",
+			in:   (*int)(nil),
+			exp:  "0",
+		},
+		{
+			name: "int8_ptr",
+			in:   int8Ptr(123),
+			exp:  "123",
+		},
+		{
+			name: "int8_ptr_nil",
+			in:   (*int8)(nil),
+			exp:  "0",
+		},
+		{
+			name: "int16_ptr",
+			in:   int16Ptr(123),
+			exp:  "123",
+		},
+		{
+			name: "int16_ptr_nil",
+			in:   (*int16)(nil),
+			exp:  "0",
+		},
+		{
+			name: "int32_ptr",
+			in:   int32Ptr(123),
+			exp:  "123",
+		},
+		{
+			name: "int32_ptr_nil",
+			in:   (*int32)(nil),
+			exp:  "0",
+		},
+		{
+			name: "int64_ptr",
+			in:   int64Ptr(123),
+			exp:  "123",
+		},
+		{
+			name: "int64_ptr_nil",
+			in:   (*int64)(nil),
+			exp:  "0",
+		},
+		{
+			name: "uint_ptr",
+			in:   uintPtr(123),
+			exp:  "123",
+		},
+		{
+			name: "uint_ptr_nil",
+			in:   (*uint)(nil),
+			exp:  "0",
+		},
+		{
+			name: "uint8_ptr",
+			in:   uint8Ptr(123),
+			exp:  "123",
+		},
+		{
+			name: "uint8_ptr_nil",
+			in:   (*uint8)(nil),
+			exp:  "0",
+		},
+		{
+			name: "uint16_ptr",
+			in:   uint16Ptr(123),
+			exp:  "123",
+		},
+		{
+			name: "uint16_ptr_nil",
+			in:   (*uint16)(nil),
+			exp:  "0",
+		},
+		{
+			name: "uint32_ptr",
+			in:   uint32Ptr(123),
+			exp:  "123",
+		},
+		{
+			name: "uint32_ptr_nil",
+			in:   (*uint32)(nil),
+			exp:  "0",
+		},
+		{
+			name: "uint64_ptr",
+			in:   uint64Ptr(123),
+			exp:  "123",
+		},
+		{
+			name: "uint64_ptr_nil",
+			in:   (*uint64)(nil),
+			exp:  "0",
+		},
+	}
+
+	for _, tc := range cases {
+		tc := tc
+
+		t.Run(tc.name, func(t *testing.T) {
+			t.Parallel()
+
+			got, err := deref(tc.in)
+			if (err != nil) != tc.err {
+				t.Fatal(err)
+			}
+
+			if got, want := got, tc.exp; got != want {
+				t.Errorf("expected %q to be %q", got, want)
+			}
+		})
+	}
+}