(go + gin) sessionを利用したMiddlewareの単体テスト


ginでsessionを利用した認証ミドルウェアのテストコードの実装について

スマートな方法が思いつかず、力ずくな実装に。。
どなたかいい方法があるよっていう方は、コメントにてご教示ください。
よろしくお願いします。


## テスト対象コード

auth_middleware.go

リクエストのセッション情報を取得し、認証が成功すれば次のハンドラーへ渡すのみ

```go
func AuthMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		session := sessions.Default(c)
		authUserJson := session.Get("userInfo")
		if authUserJson == nil {
			c.JSON(401, domain.ErrorResponse{Message: "unauthorized"})
			c.Abort()
			return
		}
		var authUser domain.User
		if err := json.Unmarshal([]byte(authUserJson.(string)), &authUser); err != nil {
			c.JSON(401, domain.ErrorResponse{Message: "unauthorized"})
			c.Abort()
			return
		}
		c.Next()
	}
}
```

## テストコード

### 方針

1. セッション発行用のエンドポイント（public）と、要認証のエンドポイント（protected）を用意
1. publicへリクエスト発行し、そのレスポンスからセッションを取得
1. 取得したセッションをprotectedのリクエストのクッキーとして設定し、リクエスト発行
1. アサート

### エンドポイント

public
セッション(ID)発行用のエンドポイント

```go
// public route
public := r.Group("/public")
public.GET("", func(c *gin.Context) {
    user := domain.User{
        ID:       1,
        Name:     "test",
        Email:    "email",
        Password: "password",
    }
    session := sessions.Default(c)
    bUser, _ := json.Marshal(user)
    session.Set("userInfo", string(bUser))
    session.Save()
    c.JSON(http.StatusOK, gin.H{"message": "success"})
})
```

protected
    認証用ミドルウェアを通過するエンドポイント

```go
// protected route
protected := r.Group("/protected")
protected.Use(middleware.AuthMiddleware())
protected.GET("", func(c *gin.Context) {
    c.JSON(http.StatusOK, gin.H{"message": "success"})
```

### リクエスト

publicへリクエスト発行し、そのレスポンスからセッションを取得

```go
// request to the public endpoint
wPub := httptest.NewRecorder()
reqPub, _ := http.NewRequest("GET", "/public", nil)
reqPub.Header.Set("Content-Type", "application/json")
r.ServeHTTP(wPub, reqPub)
```

取得したセッションをprotectedのリクエストのクッキーとして設定し、リクエスト発行

```go
// Extract session cookie from the response
sessionCookie := wPub.Header().Get("Set-Cookie")
assert.NotEmpty(t, sessionCookie, "Session cookie should not be empty")

// request to the protected endpoint
w2 := httptest.NewRecorder()
req2, _ := http.NewRequest("GET", "/protected", nil)
req2.Header.Set("Content-Type", "application/json")
req2.Header.Set("Cookie", sessionCookie) // Attach session cookie
r.ServeHTTP(w2, req2)
```

### テストコード全体

```go
func TestAuthMiddleware(t *testing.T) {
	gin.SetMode(gin.TestMode)

	// session store
	store := cookie.NewStore([]byte("secret"))

	// router
	r := gin.Default()
	// session middleware
	r.Use(sessions.Sessions("sessionid", store))

	// public route
	public := r.Group("/public")
	public.GET("", func(c *gin.Context) {
		user := domain.User{
			ID:       1,
			Name:     "test",
			Email:    "email",
			Password: "password",
		}
		session := sessions.Default(c)
		bUser, _ := json.Marshal(user)
		session.Set("userInfo", string(bUser))
		session.Save()
		c.JSON(http.StatusOK, gin.H{"message": "success"})
	})

	// protected route
	protected := r.Group("/protected")
	protected.Use(middleware.AuthMiddleware())
	protected.GET("", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{"message": "success"})
	})

	// test suite
	t.Run("authentication success", func(t *testing.T) {
		// request to the public endpoint
		wPub := httptest.NewRecorder()
		reqPub, _ := http.NewRequest("GET", "/public", nil)
		reqPub.Header.Set("Content-Type", "application/json")
		r.ServeHTTP(wPub, reqPub)

		// extract session cookie from the response
		sessionCookie := wPub.Header().Get("Set-Cookie")
		assert.NotEmpty(t, sessionCookie, "Session cookie should not be empty")

		// request to the protected endpoint
		w2 := httptest.NewRecorder()
		req2, _ := http.NewRequest("GET", "/protected", nil)
		req2.Header.Set("Content-Type", "application/json")
		req2.Header.Set("Cookie", sessionCookie) // Attach session cookie
		r.ServeHTTP(w2, req2)

		// assert
		assert.Equal(t, http.StatusOK, w2.Code)
		assert.JSONEq(t, `{"message":"success"}`, w2.Body.String())
	})
}

```