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
This commit is contained in:
parent
aa1f4a91bf
commit
0b67765510
11 changed files with 21 additions and 0 deletions
280
test/lambda_test.go
Normal file
280
test/lambda_test.go
Normal file
|
|
@ -0,0 +1,280 @@
|
|||
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")
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue