Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

History

24 Commits

Repository files navigation

Mailpit Go API Client

Go ReferenceGo Report CardLicensecodecov

A production-ready Go client library for the Mailpit API, providing 100% coverage of all Mailpit API endpoints. Mailpit is a popular email testing tool with a REST API for managing emails, messages, tags, and server operations.

✨ Features

  • 🚀 Production-ready with comprehensive error handling and retry logic
  • 📡 100% API coverage - All Mailpit endpoints implemented and tested
  • 🔄 Context support for cancellation and timeouts
  • 🔒 TLS/HTTPS support with mkcert integration for testing
  • High performance with connection pooling and optimizations
  • 🧪 Comprehensive testing with unit tests and E2E testing via testcontainers
  • 🔧 Thread-safe - Safe for concurrent use across goroutines
  • 📚 Well-documented with extensive examples and godoc comments

📋 Requirements

  • Go 1.25.0 or later
  • Mailpit server (for testing/production use)

🚀 Installation

go get github.com/CodeLieutenant/mailpitclient

🏃 Quick Start

Basic Usage

package main
import (
"context""fmt""log"
mailpit "github.com/CodeLieutenant/mailpitclient"
)
funcmain() {
// Create client with default configuration (localhost:8025)client, err:=mailpit.NewClient(nil)
iferr!=nil {
log.Fatal(err)
}
deferclient.Close()
ctx:=context.Background()
// Check server healthiferr:=client.HealthCheck(ctx); err!=nil {
log.Fatal("Mailpit server not accessible:", err)
}
// Get server informationinfo, err:=client.GetServerInfo(ctx)
iferr!=nil {
log.Fatal(err)
}
fmt.Printf("Connected to Mailpit v%s\n", info.Version)
// List all messagesmessages, err:=client.ListMessages(ctx, nil)
iferr!=nil {
log.Fatal(err)
}
fmt.Printf("Found %d messages\n", messages.Total)
}

Custom Configuration

config:=&mailpit.Config{
BaseURL: "https://mailpit.example.com",
Timeout: 30*time.Second,
MaxRetries: 3,
RetryDelay: 1*time.Second,
APIKey: "your-api-key", // For authenticated instances
}
client, err:=mailpit.NewClient(config)
iferr!=nil {
log.Fatal(err)
}
deferclient.Close()

📖 Usage Examples

Message Operations

List Messages with Pagination

opts:=&mailpit.ListOptions{
Start: 0,
Limit: 10,
}
messages, err:=client.ListMessages(ctx, opts)
iferr!=nil {
log.Fatal(err)
}
for_, msg:=rangemessages.Messages {
fmt.Printf("ID: %s, Subject: %s, From: %s\n",
msg.ID, msg.Subject, msg.From.Address)
}

Get Message Details

message, err:=client.GetMessage(ctx, "message-id")
iferr!=nil {
log.Fatal(err)
}
fmt.Printf("Subject: %s\n", message.Subject)
fmt.Printf("From: %s <%s>\n", message.From.Name, message.From.Address)
fmt.Printf("Date: %s\n", message.Date.Format(time.RFC3339))
fmt.Printf("HTML Body: %s\n", message.HTML)
fmt.Printf("Text Body: %s\n", message.Text)

Search Messages

// Search by sender emailresults, err:=client.SearchMessages(ctx, "from:test@example.com", nil)
iferr!=nil {
log.Fatal(err)
}
// Search with additional optionssearchOpts:=&mailpit.SearchOptions{
Start: 0,
Limit: 20,
}
results, err=client.SearchMessages(ctx, "subject:important", searchOpts)
iferr!=nil {
log.Fatal(err)
}
fmt.Printf("Found %d matching messages\n", results.Total)

Message Analysis

// HTML validationhtmlCheck, err:=client.GetMessageHTMLCheck(ctx, "message-id")
iferr!=nil {
log.Fatal(err)
}
fmt.Printf("HTML errors: %d, warnings: %d\n",
len(htmlCheck.Errors), len(htmlCheck.Warnings))
// Link validationlinkCheck, err:=client.GetMessageLinkCheck(ctx, "message-id")
iferr!=nil {
log.Fatal(err)
}
for_, link:=rangelinkCheck.Links {
fmt.Printf("URL: %s, Status: %d, Valid: %v\n",
link.URL, link.Status, link.Valid)
}
// SpamAssassin analysissaCheck, err:=client.GetMessageSpamAssassinCheck(ctx, "message-id")
iferr!=nil {
log.Fatal(err)
}
fmt.Printf("Spam score: %.2f, Required: %.2f\n",
saCheck.Score, saCheck.RequiredScore)

Send Operations

message:=&mailpit.SendMessageRequest{
From: mailpit.Address{
Address: "sender@example.com",
Name: "Test Sender",
},
To: []mailpit.Address{
{Address: "recipient@example.com", Name: "Test Recipient"},
},
Cc: []mailpit.Address{
{Address: "cc@example.com", Name: "CC Recipient"},
},
Subject: "Test Message from Go Client",
HTML: "<h1>Hello World</h1><p>This is a <strong>test</strong> message.</p>",
Text: "Hello World\n\nThis is a test message.",
Tags: []string{"test", "automated", "go-client"},
}
result, err:=client.SendMessage(ctx, message)
iferr!=nil {
log.Fatal(err)
}
fmt.Printf("Message sent with ID: %s\n", result.ID)

Tag Operations

// Get all available tagstags, err:=client.GetTags(ctx)
iferr!=nil {
log.Fatal(err)
}
fmt.Printf("Available tags: %v\n", tags)
// Set global tagsnewTags:= []string{"important", "test", "automated", "production"}
updatedTags, err:=client.SetTags(ctx, newTags)
iferr!=nil {
log.Fatal(err)
}
fmt.Printf("Updated global tags: %v\n", updatedTags)
// Tag specific messagesmessageIDs:= []string{"msg-1", "msg-2", "msg-3"}
err=client.SetMessageTags(ctx, "urgent", messageIDs)
iferr!=nil {
log.Fatal(err)
}
fmt.Println("Messages tagged as 'urgent'")
// Delete a tagerr=client.DeleteTag(ctx, "old-tag")
iferr!=nil {
log.Fatal(err)
}

Server Operations

// Get comprehensive server informationinfo, err:=client.GetServerInfo(ctx)
iferr!=nil {
log.Fatal(err)
}
fmt.Printf("Mailpit Version: %s\n", info.Version)
fmt.Printf("Database: %s\n", info.Database)
// Get server statisticsstats, err:=client.GetStats(ctx)
iferr!=nil {
log.Fatal(err)
}
fmt.Printf("Total messages: %d\n", stats.Total)
fmt.Printf("Unread messages: %d\n", stats.Unread)
// Get Web UI configurationwebConfig, err:=client.GetWebUIConfig(ctx)
iferr!=nil {
log.Fatal(err)
}
fmt.Printf("Read-only mode: %v\n", webConfig.ReadOnly)
fmt.Printf("SMTP server enabled: %v\n", webConfig.SMTPEnabled)

Advanced Features

Message Release (SMTP Relay)

releaseReq:=&mailpit.ReleaseMessageRequest{
To: []string{"recipient@production.com"},
Host: "smtp.gmail.com",
Port: 587,
Auth: &mailpit.SMTPAuth{
Username: "your-email@gmail.com",
Password: "app-password",
AuthType: "PLAIN",
},
TLS: true,
}
err:=client.ReleaseMessage(ctx, "message-id", releaseReq)
iferr!=nil {
log.Fatal(err)
}
fmt.Println("Message released via SMTP")

Chaos Testing (for resilience testing)

// Get current chaos configurationchaosConfig, err:=client.GetChaosConfig(ctx)
iferr!=nil {
log.Fatal(err)
}
fmt.Printf("Chaos testing enabled: %v\n", chaosConfig.Enabled)
// Configure chaos triggerstriggers:=&mailpit.ChaosTriggers{
AcceptConnections: 0.9, // 90% success rateRejectSenders: 0.1, // 10% sender rejectionDelayConnections: 0.2, // 20% connection delayDelayDuration: 5, // 5 second delays
}
updated, err:=client.SetChaosConfig(ctx, triggers)
iferr!=nil {
log.Fatal(err)
}
fmt.Printf("Chaos triggers updated: %+v\n", updated)

Error Handling

message, err:=client.GetMessage(ctx, "invalid-id")
iferr!=nil {
varmailpitErr*mailpit.Erroriferrors.As(err, &mailpitErr) {
switchmailpitErr.Type {
casemailpit.ErrorTypeAPI:
ifmailpitErr.StatusCode==404 {
fmt.Println("Message not found")
} else {
fmt.Printf("API error %d: %s\n", mailpitErr.StatusCode, mailpitErr.Message)
}
casemailpit.ErrorTypeNetwork:
fmt.Println("Network error:", mailpitErr.Message)
casemailpit.ErrorTypeValidation:
fmt.Println("Validation error:", mailpitErr.Message)
casemailpit.ErrorTypeTimeout:
fmt.Println("Request timed out:", mailpitErr.Message)
}
} else {
fmt.Println("Unknown error:", err)
}
}

🧪 Testing Integration

This client is designed for seamless integration with testcontainers for comprehensive testing:

funcTestWithMailpit(t*testing.T) {
ctx:=context.Background()
// Start Mailpit containercontainer, err:=testcontainers.GenericContainer(ctx,
testcontainers.GenericContainerRequest{
ContainerRequest: testcontainers.ContainerRequest{
Image: "axllent/mailpit:latest",
ExposedPorts: []string{"8025/tcp", "1025/tcp"},
WaitingFor: wait.ForHTTP("/api/v1/info").WithPort("8025/tcp"),
Env: map[string]string{
"MP_SMTP_AUTH_ACCEPT_ANY": "1",
"MP_SMTP_AUTH_ALLOW_INSECURE": "1",
},
},
Started: true,
})
require.NoError(t, err)
defercontainer.Terminate(ctx)
// Get container connection detailshost, err:=container.Host(ctx)
require.NoError(t, err)
httpPort, err:=container.MappedPort(ctx, "8025")
require.NoError(t, err)
smtpPort, err:=container.MappedPort(ctx, "1025")
require.NoError(t, err)
// Create API clientclient, err:=mailpit.NewClient(&mailpit.Config{
BaseURL: fmt.Sprintf("http://%s:%s", host, httpPort.Port()),
Timeout: 10*time.Second,
})
require.NoError(t, err)
deferclient.Close()
// Send test email via SMTPsmtpClient, err:=smtp.Dial(fmt.Sprintf("%s:%s", host, smtpPort.Port()))
require.NoError(t, err)
defersmtpClient.Close()
err=smtpClient.Mail("test@example.com")
require.NoError(t, err)
err=smtpClient.Rcpt("recipient@example.com")
require.NoError(t, err)
writer, err:=smtpClient.Data()
require.NoError(t, err)
_, err=writer.Write([]byte(`Subject: Test EmailFrom: test@example.comTo: recipient@example.comThis is a test email body.`))
require.NoError(t, err)
require.NoError(t, writer.Close())
// Wait for message processingtime.Sleep(2*time.Second)
// Verify message receivedmessages, err:=client.ListMessages(ctx, nil)
require.NoError(t, err)
require.Equal(t, 1, messages.Total)
message:=messages.Messages[0]
assert.Equal(t, "Test Email", message.Subject)
assert.Equal(t, "test@example.com", message.From.Address)
}

📊 API Coverage

This client provides 100% coverage of the Mailpit API endpoints. For detailed endpoint mapping and implementation status, see our API Coverage Documentation.

Automated Coverage Testing

We maintain automated testing to ensure complete API coverage:

# Run full API coverage validation
make test-api-coverage
# Run fast offline coverage test
make test-api-coverage-offline

🛠️ Development

Prerequisites

Development Commands

# Install dependencies
go mod download
# Run tests
make test# Run linting
make check
# Auto-fix lint issues
make fix
# Run field alignment optimization
make fieldalign
# Security scanning
make security
# Generate TLS certificates for testing
make mkcert-generate HOSTS='localhost 127.0.0.1 ::1'

Testing

The project includes comprehensive testing:

  • Unit Tests: Test individual functions and methods
  • Integration Tests: End-to-end testing with real Mailpit instances
  • API Coverage Tests: Automated verification of complete API coverage
# Run all tests
make test# Run with coverage
go test -race -coverprofile=coverage.out ./...
# View coverage report
go tool cover -html=coverage.out

📚 Documentation

🤝 Contributing

Contributions are welcome! Please follow these guidelines:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Run tests and linting (make check test)
  5. Push to the branch (git push origin feature/amazing-feature)
  6. Open a Pull Request

Code Standards

  • Follow the established patterns in the codebase
  • Add tests for new functionality (unit + E2E)
  • All tests must use t.Parallel() for parallel execution
  • Maintain >90% code coverage
  • Use make check to validate code quality
  • Update documentation as needed

📄 License

This project is licensed under the Apache License 2.0. See the LICENSE file for details.

🔗 Related Projects

  • Mailpit - The email testing tool this client connects to
  • Testcontainers Go - Docker testing framework used in our test suite

⭐ Support

If you find this project helpful, please consider giving it a star on GitHub!

For bugs, feature requests, or questions, please open an issue.


Built with ❤️ for the Go community

About

No description, website, or topics provided.

Resources

Stars

4 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages