Add a few tests
This commit is contained in:
316
tesseract/genconf/html_test.go
Normal file
316
tesseract/genconf/html_test.go
Normal file
@@ -0,0 +1,316 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestGenerateHTML(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
|
||||
// Create test directories
|
||||
testLogDir := filepath.Join(tmpDir, "test-log")
|
||||
err := os.MkdirAll(testLogDir, 0755)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Generate test private key
|
||||
privKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
privKeyDER, err := x509.MarshalECPrivateKey(privKey)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
privKeyPEM := pem.EncodeToMemory(&pem.Block{
|
||||
Type: "EC PRIVATE KEY",
|
||||
Bytes: privKeyDER,
|
||||
})
|
||||
|
||||
keyFile := filepath.Join(tmpDir, "test-key.pem")
|
||||
err = os.WriteFile(keyFile, privKeyPEM, 0600)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Create test config
|
||||
configContent := `listen:
|
||||
- ":8080"
|
||||
logs:
|
||||
- shortname: "test-log"
|
||||
submissionprefix: "https://example.com/submit"
|
||||
monitoringprefix: "https://example.com/monitor"
|
||||
secret: "` + keyFile + `"
|
||||
localdirectory: "` + testLogDir + `"
|
||||
notafterstart: "2024-01-01T00:00:00Z"
|
||||
notafterlimit: "2025-01-01T00:00:00Z"`
|
||||
|
||||
configFile := filepath.Join(tmpDir, "test-config.yaml")
|
||||
err = os.WriteFile(configFile, []byte(configContent), 0644)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Run generateHTML
|
||||
generateHTML(configFile, false, true, false)
|
||||
|
||||
// Verify HTML file was created
|
||||
htmlFile := filepath.Join(testLogDir, "index.html")
|
||||
htmlContent, err := os.ReadFile(htmlFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to read HTML file: %v", err)
|
||||
}
|
||||
|
||||
htmlStr := string(htmlContent)
|
||||
|
||||
// Check HTML contains expected elements
|
||||
if !strings.Contains(htmlStr, "<!DOCTYPE html>") {
|
||||
t.Error("Expected HTML doctype")
|
||||
}
|
||||
if !strings.Contains(htmlStr, "TesseraCT") {
|
||||
t.Error("Expected TesseraCT title")
|
||||
}
|
||||
if !strings.Contains(htmlStr, "example.com") {
|
||||
t.Error("Expected origin hostname in HTML")
|
||||
}
|
||||
if !strings.Contains(htmlStr, "https://example.com/submit/") {
|
||||
t.Error("Expected submission prefix in HTML")
|
||||
}
|
||||
if !strings.Contains(htmlStr, "https://example.com/monitor/") {
|
||||
t.Error("Expected monitoring prefix in HTML")
|
||||
}
|
||||
if !strings.Contains(htmlStr, "2024-01-01T00:00:00Z") {
|
||||
t.Error("Expected start time in HTML")
|
||||
}
|
||||
if !strings.Contains(htmlStr, "2025-01-01T00:00:00Z") {
|
||||
t.Error("Expected end time in HTML")
|
||||
}
|
||||
if !strings.Contains(htmlStr, "-----BEGIN PUBLIC KEY-----") {
|
||||
t.Error("Expected public key in HTML")
|
||||
}
|
||||
|
||||
// Verify JSON file was created
|
||||
jsonFile := filepath.Join(testLogDir, "log.v3.json")
|
||||
jsonContent, err := os.ReadFile(jsonFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to read JSON file: %v", err)
|
||||
}
|
||||
|
||||
var logJSON LogV3JSON
|
||||
err = json.Unmarshal(jsonContent, &logJSON)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to parse JSON: %v", err)
|
||||
}
|
||||
|
||||
// Verify JSON content
|
||||
if logJSON.Description != "example.com" {
|
||||
t.Errorf("Expected description 'example.com', got %s", logJSON.Description)
|
||||
}
|
||||
if logJSON.SubmissionURL != "https://example.com/submit/" {
|
||||
t.Errorf("Expected submission URL 'https://example.com/submit/', got %s", logJSON.SubmissionURL)
|
||||
}
|
||||
if logJSON.MonitoringURL != "https://example.com/monitor/" {
|
||||
t.Errorf("Expected monitoring URL 'https://example.com/monitor/', got %s", logJSON.MonitoringURL)
|
||||
}
|
||||
if logJSON.TemporalInterval.StartInclusive != "2024-01-01T00:00:00Z" {
|
||||
t.Errorf("Expected start time '2024-01-01T00:00:00Z', got %s", logJSON.TemporalInterval.StartInclusive)
|
||||
}
|
||||
if logJSON.TemporalInterval.EndExclusive != "2025-01-01T00:00:00Z" {
|
||||
t.Errorf("Expected end time '2025-01-01T00:00:00Z', got %s", logJSON.TemporalInterval.EndExclusive)
|
||||
}
|
||||
if logJSON.MMD != 60 {
|
||||
t.Errorf("Expected MMD 60, got %d", logJSON.MMD)
|
||||
}
|
||||
if logJSON.LogID == "" {
|
||||
t.Error("Expected non-empty LogID")
|
||||
}
|
||||
if logJSON.Key == "" {
|
||||
t.Error("Expected non-empty Key")
|
||||
}
|
||||
}
|
||||
|
||||
func TestComputeKeyInfo(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
|
||||
// Generate test private key
|
||||
privKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
privKeyDER, err := x509.MarshalECPrivateKey(privKey)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
privKeyPEM := pem.EncodeToMemory(&pem.Block{
|
||||
Type: "EC PRIVATE KEY",
|
||||
Bytes: privKeyDER,
|
||||
})
|
||||
|
||||
keyFile := filepath.Join(tmpDir, "test-key.pem")
|
||||
err = os.WriteFile(keyFile, privKeyPEM, 0600)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Create log entry
|
||||
logEntry := Log{
|
||||
ShortName: "test-log",
|
||||
Secret: keyFile,
|
||||
}
|
||||
|
||||
// Compute key info
|
||||
err = computeKeyInfo(&logEntry)
|
||||
if err != nil {
|
||||
t.Fatalf("computeKeyInfo() error = %v", err)
|
||||
}
|
||||
|
||||
// Verify computed fields
|
||||
if logEntry.LogID == "" {
|
||||
t.Error("Expected non-empty LogID")
|
||||
}
|
||||
if logEntry.PublicKeyPEM == "" {
|
||||
t.Error("Expected non-empty PublicKeyPEM")
|
||||
}
|
||||
if !strings.Contains(logEntry.PublicKeyPEM, "-----BEGIN PUBLIC KEY-----") {
|
||||
t.Error("Expected PEM format public key")
|
||||
}
|
||||
if !strings.Contains(logEntry.PublicKeyPEM, "-----END PUBLIC KEY-----") {
|
||||
t.Error("Expected PEM format public key")
|
||||
}
|
||||
if logEntry.PublicKeyDERB64 == "" {
|
||||
t.Error("Expected non-empty PublicKeyDERB64")
|
||||
}
|
||||
if logEntry.PublicKeyBase64 == "" {
|
||||
t.Error("Expected non-empty PublicKeyBase64")
|
||||
}
|
||||
if logEntry.PublicKeyDERB64 != logEntry.PublicKeyBase64 {
|
||||
t.Error("Expected PublicKeyDERB64 and PublicKeyBase64 to be the same")
|
||||
}
|
||||
}
|
||||
|
||||
func TestComputeKeyInfoInvalidKey(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
|
||||
// Create invalid key file
|
||||
invalidKeyFile := filepath.Join(tmpDir, "invalid-key.pem")
|
||||
invalidKeyContent := `-----BEGIN EC PRIVATE KEY-----
|
||||
INVALID KEY DATA
|
||||
-----END EC PRIVATE KEY-----`
|
||||
|
||||
err := os.WriteFile(invalidKeyFile, []byte(invalidKeyContent), 0600)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
logEntry := Log{
|
||||
Secret: invalidKeyFile,
|
||||
}
|
||||
|
||||
err = computeKeyInfo(&logEntry)
|
||||
if err == nil {
|
||||
t.Error("Expected error for invalid key file")
|
||||
}
|
||||
}
|
||||
|
||||
func TestComputeKeyInfoMissingFile(t *testing.T) {
|
||||
logEntry := Log{
|
||||
Secret: "/nonexistent/key.pem",
|
||||
}
|
||||
|
||||
err := computeKeyInfo(&logEntry)
|
||||
if err == nil {
|
||||
t.Error("Expected error for missing key file")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateLogJSONWithStatus(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
outputFile := filepath.Join(tmpDir, "test-log.v3.json")
|
||||
|
||||
testTime1, _ := time.Parse("2006-01-02T15:04:05Z", "2024-01-01T00:00:00Z")
|
||||
testTime2, _ := time.Parse("2006-01-02T15:04:05Z", "2025-01-01T00:00:00Z")
|
||||
|
||||
logEntry := Log{
|
||||
Origin: "test.example.com",
|
||||
SubmissionPrefix: "https://test.example.com/submit",
|
||||
MonitoringPrefix: "https://test.example.com/monitor",
|
||||
NotAfterStart: testTime1,
|
||||
NotAfterLimit: testTime2,
|
||||
LogID: "dGVzdC1sb2ctaWQ=", // base64 encoded "test-log-id"
|
||||
PublicKeyBase64: "dGVzdC1wdWJsaWMta2V5", // base64 encoded "test-public-key"
|
||||
}
|
||||
|
||||
err := generateLogJSONWithStatus(logEntry, outputFile, false, true, false)
|
||||
if err != nil {
|
||||
t.Fatalf("generateLogJSONWithStatus() error = %v", err)
|
||||
}
|
||||
|
||||
// Verify JSON was created
|
||||
jsonContent, err := os.ReadFile(outputFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to read JSON file: %v", err)
|
||||
}
|
||||
|
||||
var logJSON LogV3JSON
|
||||
err = json.Unmarshal(jsonContent, &logJSON)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to parse JSON: %v", err)
|
||||
}
|
||||
|
||||
// Verify content
|
||||
if logJSON.Description != "test.example.com" {
|
||||
t.Errorf("Expected description 'test.example.com', got %s", logJSON.Description)
|
||||
}
|
||||
if logJSON.SubmissionURL != "https://test.example.com/submit/" {
|
||||
t.Errorf("Expected submission URL to have trailing slash")
|
||||
}
|
||||
if logJSON.MonitoringURL != "https://test.example.com/monitor/" {
|
||||
t.Errorf("Expected monitoring URL to have trailing slash")
|
||||
}
|
||||
if logJSON.LogID != "dGVzdC1sb2ctaWQ=" {
|
||||
t.Errorf("Expected LogID 'dGVzdC1sb2ctaWQ=', got %s", logJSON.LogID)
|
||||
}
|
||||
if logJSON.Key != "dGVzdC1wdWJsaWMta2V5" {
|
||||
t.Errorf("Expected Key 'dGVzdC1wdWJsaWMta2V5', got %s", logJSON.Key)
|
||||
}
|
||||
if logJSON.MMD != 60 {
|
||||
t.Errorf("Expected MMD 60, got %d", logJSON.MMD)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateHTMLMissingDirectory(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
|
||||
// Create config with non-existent directory
|
||||
configContent := `logs:
|
||||
- shortname: "test-log"
|
||||
submissionprefix: "https://example.com/submit"
|
||||
monitoringprefix: "https://example.com/monitor"
|
||||
secret: "test.key"
|
||||
localdirectory: "/nonexistent/directory"`
|
||||
|
||||
configFile := filepath.Join(tmpDir, "test-config.yaml")
|
||||
err := os.WriteFile(configFile, []byte(configContent), 0644)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Should call log.Fatalf which exits the program
|
||||
// We can't easily test this without subprocess, so we'll skip it
|
||||
t.Skip("Cannot easily test log.Fatalf without subprocess")
|
||||
}
|
Reference in New Issue
Block a user