content/static: add deprecated and retracted banners

When a module is deprecated or retracted, add a banner to the main
page.

Test with an integration test to verify that the
deprecation/retraction information makes it through the entire
pipeline.

For golang/go#43265
For golang/go#41321

Change-Id: I0ffd1a9d1617e2865a10f0b0a8a8a3af6ed4d420
Reviewed-on: https://go-review.googlesource.com/c/pkgsite/+/296815
Trust: Jonathan Amsterdam <jba@google.com>
Run-TryBot: Jonathan Amsterdam <jba@google.com>
TryBot-Result: kokoro <noreply+kokoro@google.com>
Reviewed-by: Julie Qiu <julie@golang.org>
diff --git a/content/static/css/unit_header.css b/content/static/css/unit_header.css
index 5c5986c..39fa864 100644
--- a/content/static/css/unit_header.css
+++ b/content/static/css/unit_header.css
@@ -109,13 +109,24 @@
 }
 
 .UnitHeader-majorVersionBanner,
-.UnitHeader-redirectedFromBanner {
-  background-color: var(--gray-10);
+.UnitHeader-redirectedFromBanner,
+.UnitHeader-deprecatedBanner,
+.UnitHeader-retractedBanner {
   display: flex;
   margin: -0.5rem 0 1rem 0;
   padding: 0.75rem 0;
 }
 
+.UnitHeader-majorVersionBanner,
+.UnitHeader-redirectedFromBanner {
+  background-color: var(--gray-10);
+}
+
+.UnitHeader-deprecatedBanner,
+.UnitHeader-retractedBanner {
+  background-color: var(--yellow);
+}
+
 .UnitHeader-detailIcon {
   color: var(--gray-3);
   flex-shrink: 0;
diff --git a/content/static/html/helpers/_unit_header.tmpl b/content/static/html/helpers/_unit_header.tmpl
index 3a7d5a7..a0a3acf 100644
--- a/content/static/html/helpers/_unit_header.tmpl
+++ b/content/static/html/helpers/_unit_header.tmpl
@@ -46,6 +46,19 @@
           </span>
         </div>
       {{end}}
+
+      {{if .Unit.Deprecated}}
+        <div class="UnitHeader-deprecatedBanner">
+          Deprecated{{with .Unit.DeprecationComment}}: {{.}}{{end}}
+	</div>
+      {{end}}
+
+      {{if .Unit.Retracted}}
+        <div class="UnitHeader-retractedBanner">
+          Retracted{{with .Unit.RetractionRationale}}: {{.}}{{end}}
+	</div>
+      {{end}}
+
       {{if .LatestMajorVersion}}
         <div class="UnitHeader-majorVersionBanner" data-test-id="UnitHeader-majorVersionBanner">
           <img height="19px" width="16px" class="UnitHeader-detailIcon" src="/static/img/pkg-icon-info_19x16.svg" alt="">
diff --git a/internal/testing/integration/integration_test.go b/internal/testing/integration/integration_test.go
index 1e72c1b..ecef188 100644
--- a/internal/testing/integration/integration_test.go
+++ b/internal/testing/integration/integration_test.go
@@ -18,6 +18,7 @@
 	"github.com/google/go-cmp/cmp"
 	"github.com/google/safehtml/template"
 	"golang.org/x/pkgsite/internal"
+	"golang.org/x/pkgsite/internal/experiment"
 	"golang.org/x/pkgsite/internal/godoc/dochtml"
 	"golang.org/x/pkgsite/internal/index"
 	"golang.org/x/pkgsite/internal/middleware"
@@ -39,6 +40,7 @@
 func TestEndToEndProcessing(t *testing.T) {
 	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 	defer cancel()
+	ctx = experiment.NewContext(ctx, internal.ExperimentRetractions)
 
 	defer postgres.ResetTestDB(testDB, t)
 
@@ -76,16 +78,20 @@
 		{"example.com/single", "This is the README"},
 		{"example.com/single/pkg", "hello"},
 		{"example.com/single@v1.0.0/pkg", "hello"},
+		{"example.com/deprecated", "UnitHeader-deprecatedBanner"},
+		{"example.com/retractions@v1.1.0", "UnitHeader-retractedBanner"},
 	} {
-		wantKeys = append(wantKeys, "/"+test.url)
-		body, err := doGet(frontendHTTP.URL + "/" + test.url)
-		if err != nil {
-			t.Fatalf("%s: %v", test.url, err)
-		}
-		if !strings.Contains(string(body), test.want) {
-			t.Errorf("%q not found in body", test.want)
-			t.Logf("%s", body)
-		}
+		t.Run(strings.ReplaceAll(test.url, "/", "_"), func(t *testing.T) {
+			wantKeys = append(wantKeys, "/"+test.url)
+			body, err := doGet(frontendHTTP.URL + "/" + test.url)
+			if err != nil {
+				t.Fatalf("%s: %v", test.url, err)
+			}
+			if !strings.Contains(string(body), test.want) {
+				t.Errorf("%q not found in body", test.want)
+				t.Logf("%s", body)
+			}
+		})
 	}
 
 	// Test cache invalidation.
@@ -105,8 +111,14 @@
 	}
 
 	// All the keys with modulePath should be gone, but the others should remain.
+	wantKeys = nil
+	// Remove modulePath from the previous keys.
+	for _, k := range keys {
+		if !strings.Contains(k, modulePath) {
+			wantKeys = append(wantKeys, k)
+		}
+	}
 	keys = cacheKeys(t, redisCacheClient)
-	wantKeys = []string{"/example.com/basic", "/example.com/basic@v1.0.0"}
 	if !cmp.Equal(keys, wantKeys) {
 		t.Errorf("cache keys: got %v, want %v", keys, wantKeys)
 	}