n-daisuke-blog-deployment-s.../test/lambda_test.go
Daisuke 0b67765510 Restructure project layout and add ECR repository CloudFormation template
- Move application entrypoint to cmd/lambda/
- Move Dockerfile to docker/ for clearer build context separation
- Promote go.mod/go.sum to project root
- Move CloudFormation templates under infra/cfn/ for consistent infra layout
- Add new template-container-repository.yaml defining ECR repository (blog-deployment)
- Move Lambda test files to test/ directory
2025-12-31 19:24:08 +09:00

280 lines
8.7 KiB
Go

package main
import (
"context"
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"fmt"
"os"
"os/exec"
"path/filepath"
"testing"
"github.com/aws/aws-sdk-go-v2/feature/s3/manager"
"github.com/aws/aws-sdk-go-v2/service/s3"
)
func TestVerifySignature_Valid(t *testing.T) {
secret := "mysecret"
body := "{\"message\":\"example\"}"
// Compute the expected signature for the valid scenario.
mac := hmac.New(sha256.New, []byte(secret))
mac.Write([]byte(body))
expectedSig := hex.EncodeToString(mac.Sum(nil))
// Prepare the signature header in the "sha256=<signature>" format.
signatureHeader := "sha256=" + expectedSig
if !verifySignature(secret, body, signatureHeader) {
t.Errorf("Expected true for valid signature, got false")
}
}
func TestVerifySignature_InvalidSignature(t *testing.T) {
secret := "mysecret"
body := "{\"message\":\"example\"}"
// Use an intentionally incorrect signature.
signatureHeader := "sha256=invalidsignature"
if verifySignature(secret, body, signatureHeader) {
t.Errorf("Expected false for an invalid signature, but got true")
}
}
func TestVerifySignature_MissingPrefix(t *testing.T) {
secret := "mysecret"
body := "{\"message\":\"example\"}"
// Provide a header that does not start with "sha256="
signatureHeader := "invalidprefix"
if verifySignature(secret, body, signatureHeader) {
t.Errorf("Expected false when header is missing the required prefix, got true")
}
}
func TestVerifySignature_EmptyHeader(t *testing.T) {
secret := "mysecret"
body := "{\"message\":\"example\"}"
signatureHeader := ""
if verifySignature(secret, body, signatureHeader) {
t.Errorf("Expected false when header is empty, got true")
}
}
func TestLoadConfig_Success(t *testing.T) {
// Set up environment variables for the test.
os.Setenv("REPO_URL", "https://example.com/repo.git")
os.Setenv("REPO_BRANCH", "main")
os.Setenv("S3_BUCKET", "my-s3-bucket")
os.Setenv("S3_KEY", "source.zip")
os.Setenv("AWS_REGION", "ap-northeast-1")
// Call the function.
cfg, err := loadConfig()
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
// Validate the configuration values.
if cfg.RepoURL != "https://example.com/repo.git" {
t.Errorf("unexpected RepoURL: got %s, want %s", cfg.RepoURL, "https://example.com/repo.git")
}
if cfg.RepoBranch != "main" {
t.Errorf("unexpected RepoBranch: got %s, want %s", cfg.RepoBranch, "main")
}
if cfg.S3Bucket != "my-s3-bucket" {
t.Errorf("unexpected S3Bucket: got %s, want %s", cfg.S3Bucket, "my-s3-bucket")
}
if cfg.S3Key != "source.zip" {
t.Errorf("unexpected S3Key: got %s, want %s", cfg.S3Key, "source.zip")
}
if cfg.AWSRegion != "ap-northeast-1" {
t.Errorf("unexpected AWSRegion: got %s, want %s", cfg.AWSRegion, "ap-northeast-1")
}
}
func TestLoadConfig_MissingRepoURL(t *testing.T) {
// Clear the REPO_URL environment variable.
os.Unsetenv("REPO_URL")
// Optionally, set up other required variables.
os.Setenv("S3_BUCKET", "my-s3-bucket")
// Call the function.
_, err := loadConfig()
if err == nil {
t.Fatal("expected an error due to missing REPO_URL, got nil")
}
}
func TestLoadConfig_MissingS3Bucket(t *testing.T) {
// Clear the S3_BUCKET environment variable.
os.Unsetenv("S3_BUCKET")
// Optionally, set up other required variables.
os.Setenv("REPO_URL", "https://example.com/repo.git")
// Call the function.
_, err := loadConfig()
if err == nil {
t.Fatal("expected an error due to missing S3_BUCKET, got nil")
}
}
// TestHelperProcess is not a real test. It is invoked as a helper process
// when our fake command runner is used.
func TestHelperProcess(t *testing.T) {
if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
// If the variable is not set, this function should do nothing.
return
}
// If GO_EXIT_STATUS is set, convert it to an integer.
if exitStatus := os.Getenv("GO_EXIT_STATUS"); exitStatus != "" {
// You can exit with this status if it's nonzero.
// For example, if it's "1", then this simulates a failing command.
os.Exit(1)
}
// Otherwise, simulate success.
os.Exit(0)
}
// fakeExecCommand returns a function that simulates exec.Command.
// The success parameter indicates whether the command should succeed.
func fakeExecCommand(success bool) func(name string, arg ...string) *exec.Cmd {
return func(name string, arg ...string) *exec.Cmd {
// We simulate the command by re-executing the test binary with special flags.
// The "--" signals the end of flags for our helper.
cs := []string{"-test.run=TestHelperProcess", "--", name}
cs = append(cs, arg...)
cmd := exec.Command(os.Args[0], cs...)
// Set an env variable that tells our TestHelperProcess to run.
cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"}
if !success {
// Set a value to indicate failure. Our helper can inspect this if desired,
// or we can simply exit with a non-zero status.
cmd.Env = append(cmd.Env, "GO_EXIT_STATUS=1")
}
return cmd
}
}
func TestCloneRepository_Success(t *testing.T) {
// Override commandRunner to simulate a successful git clone.
originalCommandRunner := commandRunner
commandRunner = fakeExecCommand(true)
defer func() { commandRunner = originalCommandRunner }()
// Use a temporary directory for testing.
tempDir := t.TempDir()
// Call cloneRepository.
err := cloneRepository(context.Background(), "https://example.com/repo.git", "main", tempDir)
if err != nil {
t.Fatalf("expected success, got error: %v", err)
}
}
func TestCloneRepository_Failure(t *testing.T) {
// Override commandRunner to simulate a failing git clone.
originalCommandRunner := commandRunner
commandRunner = fakeExecCommand(false)
defer func() { commandRunner = originalCommandRunner }()
tempDir := t.TempDir()
// Call cloneRepository expecting an error.
err := cloneRepository(context.Background(), "https://example.com/repo.git", "main", tempDir)
if err == nil {
t.Fatal("expected an error, got nil")
}
}
// TestCreateZipArchive_Success simulates a successful zip creation.
func TestCreateZipArchive_Success(t *testing.T) {
// Override the global commandRunner with our fake that simulates success.
originalCommandRunner := commandRunner
commandRunner = fakeExecCommand(true)
defer func() { commandRunner = originalCommandRunner }()
// Use t.TempDir to create a temporary directory simulating the repo directory.
tempDir := t.TempDir()
// Define a zip file path within the temp directory.
zipFilePath := filepath.Join(tempDir, "source.zip")
// Call createZipArchive.
if err := createZipArchive(context.Background(), tempDir, zipFilePath); err != nil {
t.Fatalf("expected success, got error: %v", err)
}
}
// TestCreateZipArchive_Failure simulates a failing zip creation.
func TestCreateZipArchive_Failure(t *testing.T) {
// Override commandRunner to simulate a command failure.
originalCommandRunner := commandRunner
commandRunner = fakeExecCommand(false)
defer func() { commandRunner = originalCommandRunner }()
tempDir := t.TempDir()
zipFilePath := filepath.Join(tempDir, "source.zip")
// Call createZipArchive and expect an error.
if err := createZipArchive(context.Background(), tempDir, zipFilePath); err == nil {
t.Fatal("expected an error, got nil")
}
}
// fakeUploader implements the Uploader interface.
type fakeUploader struct {
success bool
}
func (f *fakeUploader) Upload(ctx context.Context, input *s3.PutObjectInput, opts ...func(*manager.Uploader)) (*manager.UploadOutput, error) {
if f.success {
// Simulate a successful upload with a fake location.
return &manager.UploadOutput{Location: "http://fake-s3/uploaded-file"}, nil
}
// Simulate a failure.
return nil, fmt.Errorf("fake upload error")
}
func TestUploadToS3WithUploader_Success(t *testing.T) {
ctx := context.Background()
// Create a temporary directory and file to act as the ZIP file.
tempDir := t.TempDir()
zipFilePath := filepath.Join(tempDir, "source.zip")
content := []byte("dummy zip content")
if err := os.WriteFile(zipFilePath, content, 0644); err != nil {
t.Fatalf("failed to create temp zip file: %v", err)
}
// Create a fake uploader that simulates success.
u := &fakeUploader{success: true}
err := uploadToS3WithUploader(ctx, zipFilePath, "fake-bucket", "source.zip", u)
if err != nil {
t.Fatalf("expected success, got error: %v", err)
}
}
func TestUploadToS3WithUploader_Failure(t *testing.T) {
ctx := context.Background()
tempDir := t.TempDir()
zipFilePath := filepath.Join(tempDir, "source.zip")
content := []byte("dummy zip content")
if err := os.WriteFile(zipFilePath, content, 0644); err != nil {
t.Fatalf("failed to create temp zip file: %v", err)
}
// Create a fake uploader that simulates a failure.
u := &fakeUploader{success: false}
err := uploadToS3WithUploader(ctx, zipFilePath, "fake-bucket", "source.zip", u)
if err == nil {
t.Fatal("expected an error, got nil")
}
}