Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 8 additions & 58 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,31 +23,15 @@ smtp4dev is a fake SMTP email server for development and testing, built as a .NE
- Takes approximately 40-60 seconds
- NEVER CANCEL - Set timeout to 120+ seconds

2. **Install frontend dependencies:**
```bash
cd Rnwood.Smtp4dev/ClientApp
npm install
```
- Takes approximately 35-50 seconds
- NEVER CANCEL - Set timeout to 120+ seconds
- May show peer dependency warnings (expected and harmless)

3. **Build the application:**
2. **Build the application:**
```bash
# Build main web application (recommended for development)
dotnet build Rnwood.Smtp4dev/Rnwood.Smtp4dev.csproj -c Release
dotnet build Rnwood.Smtp4dev/Rnwood.Smtp4dev.csproj
```
- Takes approximately 3-5 seconds after initial frontend build
- NEVER CANCEL - Set timeout to 60+ seconds
- The Desktop project will fail on Linux (expected - Windows-only)

4. **Publish the application:**
```bash
dotnet publish Rnwood.Smtp4dev/Rnwood.Smtp4dev.csproj -c Release -o ./published
```
- Takes approximately 20-25 seconds including frontend Vite build
- NEVER CANCEL - Set timeout to 120+ seconds

### Run the Application

**Development mode (with live reload):**
Expand All @@ -57,6 +41,9 @@ dotnet run --project Rnwood.Smtp4dev/Rnwood.Smtp4dev.csproj --urls="http://local

**Published mode:**
```bash

dotnet publish Rnwood.Smtp4dev/Rnwood.Smtp4dev.csproj -c Release -o ./published

cd ./published
./Rnwood.Smtp4dev --urls="http://localhost:5000" --smtpport=2525 --imapport=1143
```
Expand Down Expand Up @@ -92,12 +79,6 @@ npm run lint
- Takes approximately 10-15 seconds
- Some configuration warnings are expected

**Frontend Tests:**
```bash
cd Rnwood.Smtp4dev/ClientApp
npm test -- --passWithNoTests
```
- Currently has Jest configuration issues (known limitation)

### Validation Scenarios

Expand Down Expand Up @@ -168,16 +149,11 @@ npm test -- --passWithNoTests
3. **Always run validation scenarios** after changes
4. Test both development and published modes

### Frontend Development
```bash
cd Rnwood.Smtp4dev/ClientApp
npm run dev # Development server with hot reload (runs on port 5173)
```

### Backend Development
### Full Stack Development
```bash
dotnet watch run --project Rnwood.Smtp4dev/Rnwood.Smtp4dev.csproj
```
Both the front end and backend will reload on changes.

### Configuration
- Default configuration in `appsettings.json` with extensive inline documentation
Expand Down Expand Up @@ -248,7 +224,6 @@ type(optional-scope): description
- **Database locks**: Application creates SQLite database in user config directory

### Testing Issues
- **Jest configuration errors**: Known issue with current module configuration
- **E2E test complexity**: Requires Chrome and specific environment setup
- **Network-dependent tests**: May fail in restricted environments

Expand All @@ -263,29 +238,4 @@ The project uses Azure Pipelines (`azure-pipelines.yml`) with:
**Build matrix includes:**
- `win-x64`, `linux-x64`, `linux-musl-x64`, `linux-arm`, `win-arm64`
- Desktop variants (Windows only)
- Docker images (Linux and Windows)

## Command Reference

### Essential Commands
```bash
# Quick development setup
dotnet restore && cd Rnwood.Smtp4dev/ClientApp && npm install && cd ../..

# Build for development
dotnet build Rnwood.Smtp4dev/Rnwood.Smtp4dev.csproj -c Release

# Run with development settings
dotnet run --project Rnwood.Smtp4dev/Rnwood.Smtp4dev.csproj --urls="http://localhost:5000" --smtpport=2525

# Test SMTP functionality
echo -e "HELO test\nMAIL FROM:<test@example.com>\nRCPT TO:<user@example.com>\nDATA\nSubject: Test\n\nTest message\n.\nQUITS" | nc localhost 2525

# Check web UI
curl -s http://localhost:5000/ && echo "Web UI is running"

# Check API
curl -s http://localhost:5000/api/messages | jq '.results | length'
```

Always ensure the application is fully functional by testing email sending, web UI access, and API responses after making any changes.
- Docker images (Linux and Windows)
1 change: 1 addition & 0 deletions Dockerfile.linux
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ ENV DOTNET_USE_POLLING_FILE_WATCHER true
EXPOSE 80
EXPOSE 25
EXPOSE 143
EXPOSE 110
ENTRYPOINT ["dotnet", "/app/Rnwood.Smtp4dev.dll"]
1 change: 1 addition & 0 deletions Dockerfile.linux.arm64
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ ENV DOTNET_USE_POLLING_FILE_WATCHER true
EXPOSE 80
EXPOSE 25
EXPOSE 143
EXPOSE 110
ENTRYPOINT ["dotnet", "/app/Rnwood.Smtp4dev.dll"]
1 change: 1 addition & 0 deletions Dockerfile.windows.ltsc2019
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ COPY out /app
EXPOSE 80
EXPOSE 25
EXPOSE 143
EXPOSE 110
ENV ASPNETCORE_HTTP_PORTS 80
ENV SERVEROPTIONS__URLS http://*:80
VOLUME c:\smtp4dev
Expand Down
1 change: 1 addition & 0 deletions Dockerfile.windows.ltsc2022
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ COPY out /app
EXPOSE 80
EXPOSE 25
EXPOSE 143
EXPOSE 110
ENV ASPNETCORE_HTTP_PORTS 80
ENV SERVEROPTIONS__URLS http://*:80
VOLUME c:\smtp4dev
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ A dummy SMTP server for Windows, Linux, Mac OS-X (and maybe elsewhere where .NET

smtp4dev support many advanced features:
- OpenAPI/Swagger API
- IMAP access to retrieve and delete messages
- IMAP and POP3 access to retrieve and delete messages
- SMTP session logging
- UTF8 support
- Viewport size switcher to simulate mobile etc
Expand Down
10 changes: 10 additions & 0 deletions Rnwood.Smtp4dev.Tests/E2E/E2ECollection.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using Xunit;

namespace Rnwood.Smtp4dev.Tests.E2E
{
[CollectionDefinition("E2E", DisableParallelization = true)]
public class E2ECollectionDefinition
{
// Collection definition intentionally left empty - used to disable parallelization for E2E tests
}
}
47 changes: 42 additions & 5 deletions Rnwood.Smtp4dev.Tests/E2E/E2ETests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ namespace Rnwood.Smtp4dev.Tests.E2E
{
public class E2ETests
{
private readonly ITestOutputHelper output;
protected readonly ITestOutputHelper output;

public E2ETests(ITestOutputHelper output)
{
Expand All @@ -37,6 +37,8 @@ public class E2ETestContext
public int SmtpPortNumber { get; set; }

public int ImapPortNumber { get; set; }
public int Pop3PortNumber { get; set; }
public string Pop3Host { get; set; } = "localhost";
}


Expand Down Expand Up @@ -117,6 +119,11 @@ protected void RunE2ETest(Action<E2ETestContext> test, E2ETestOptions options =
args.Add("--imapport=0");
}

if (!args.Any(a => a.StartsWith("--pop3port")))
{
args.Add("--pop3port=0");
}

if (!args.Any(a => a.StartsWith("--smtpport")))
{
args.Add("--smtpport=0");
Expand All @@ -134,9 +141,9 @@ protected void RunE2ETest(Action<E2ETestContext> test, E2ETestOptions options =



if (!string.IsNullOrEmpty(options.BasePath))
if (string.IsNullOrEmpty(options.BasePath))
{
args.Add($"--basepath={options.BasePath}");
args.Add("--basepath=");
}

output.WriteLine("Args: " + string.Join(" ", args.Select(a => $"\"{a}\"")));
Expand All @@ -154,9 +161,11 @@ protected void RunE2ETest(Action<E2ETestContext> test, E2ETestOptions options =
Uri baseUrl = null;
int? smtpPortNumber = null;
int? imapPortNumber = null;
int? pop3PortNumber = null;
string pop3Host = "localhost";


while ((baseUrl == null || !smtpPortNumber.HasValue || !imapPortNumber.HasValue) && serverOutput.MoveNext())
while ((baseUrl == null || !smtpPortNumber.HasValue || !imapPortNumber.HasValue || !pop3PortNumber.HasValue) && serverOutput.MoveNext())
{
string newLine = serverOutput.Current;
output.WriteLine(newLine);
Expand All @@ -183,6 +192,31 @@ protected void RunE2ETest(Action<E2ETestContext> test, E2ETestOptions options =
imapPortNumber = (binary == "docker" && internalImapPort == 143) ? 1143 : internalImapPort;
}

if (newLine.StartsWith("POP3 Server is listening on port"))
{
int internalPop3Port = int.Parse(Regex.Replace(newLine, @"POP3 Server is listening on port (\d+).*", "$1"));
// For Docker, map internal port 110 to external port 1100
pop3PortNumber = (binary == "docker" && internalPop3Port == 110) ? 1100 : internalPop3Port;
// Try to parse the address from the same line (e.g. "POP3 Server is listening on port 53333 (::)")
var m = Regex.Match(newLine, @"POP3 Server is listening on port \d+ \(([^)]+)\)");
if (m.Success)
{
var addr = m.Groups[1].Value;
if (addr == "::" || addr == "::1" || addr.Contains(':'))
{
pop3Host = "::1";
}
else if (addr == "0.0.0.0")
{
pop3Host = "127.0.0.1";
}
else
{
pop3Host = addr;
}
}
}

if (newLine.StartsWith("Application started. Press Ctrl+C to shut down."))
{
throw new Exception($@"Startup completed but did not catch variables from startup output:
Expand All @@ -207,11 +241,14 @@ protected void RunE2ETest(Action<E2ETestContext> test, E2ETestOptions options =
Assert.False(serverProcess.Process.HasExited, "Server process failed");


output.WriteLine($"Using Pop3Host: {pop3Host}, Pop3Port: {pop3PortNumber}");
test(new E2ETestContext
{
BaseUrl = baseUrl,
SmtpPortNumber = smtpPortNumber.Value,
ImapPortNumber = imapPortNumber.Value
ImapPortNumber = imapPortNumber.Value,
Pop3PortNumber = pop3PortNumber.Value,
Pop3Host = pop3Host
});
}
finally
Expand Down
Loading
Loading