diff --git a/src/Services/Notification/Notification.API/Controllers/NotificationController.cs b/src/Services/Notification/Notification.API/Controllers/NotificationController.cs
index 15d2ebc..2fb541a 100644
--- a/src/Services/Notification/Notification.API/Controllers/NotificationController.cs
+++ b/src/Services/Notification/Notification.API/Controllers/NotificationController.cs
@@ -1,3 +1,4 @@
+using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Notification.API.Services;
using Notification.Domain.Interfaces;
@@ -6,6 +7,7 @@
namespace Notification.API.Controllers;
[ApiController]
+[Authorize]
[Route("api/[controller]")]
public class NotificationController : ControllerBase
{
diff --git a/src/Services/Notification/Notification.API/Notification.API.csproj b/src/Services/Notification/Notification.API/Notification.API.csproj
index 25b5fb0..13ac5b1 100644
--- a/src/Services/Notification/Notification.API/Notification.API.csproj
+++ b/src/Services/Notification/Notification.API/Notification.API.csproj
@@ -7,10 +7,11 @@
-
-
+
+
+
diff --git a/src/Services/Notification/Notification.API/Program.cs b/src/Services/Notification/Notification.API/Program.cs
index 6219c9c..1fd2347 100644
--- a/src/Services/Notification/Notification.API/Program.cs
+++ b/src/Services/Notification/Notification.API/Program.cs
@@ -3,6 +3,9 @@
using Notification.Infrastructure.Data;
using Notification.Infrastructure.Repositories;
using Microsoft.EntityFrameworkCore;
+using Microsoft.AspNetCore.Authentication.JwtBearer;
+using Microsoft.IdentityModel.Tokens;
+using System.Text;
var builder = WebApplication.CreateBuilder(args);
@@ -11,6 +14,25 @@
builder.Services.AddSwaggerGen();
builder.Services.AddHealthChecks();
+var jwtSecret = builder.Configuration["Jwt:Secret"]
+ ?? throw new InvalidOperationException("Jwt:Secret is not configured.");
+
+builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
+ .AddJwtBearer(options =>
+ {
+ options.TokenValidationParameters = new TokenValidationParameters
+ {
+ ValidateIssuer = true,
+ ValidateAudience = true,
+ ValidateLifetime = true,
+ ValidateIssuerSigningKey = true,
+ ValidIssuer = builder.Configuration["Jwt:Issuer"],
+ ValidAudience = builder.Configuration["Jwt:Audience"],
+ IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSecret))
+ };
+ });
+builder.Services.AddAuthorization();
+
builder.Services.AddDbContext(options =>
options.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection")));
@@ -32,6 +54,9 @@
app.UseSwaggerUI();
}
+app.UseAuthentication();
+app.UseAuthorization();
+
app.MapControllers();
app.MapHealthChecks("/healthz");
diff --git a/src/Services/Notification/Notification.API/appsettings.json b/src/Services/Notification/Notification.API/appsettings.json
index 335c5c3..c8e4060 100644
--- a/src/Services/Notification/Notification.API/appsettings.json
+++ b/src/Services/Notification/Notification.API/appsettings.json
@@ -7,5 +7,10 @@
},
"ConnectionStrings": {
"DefaultConnection": "Host=localhost;Database=notificationdb;Username=postgres;Password=postgres"
+ },
+ "Jwt": {
+ "Secret": "QuickApp_Microservices_SuperSecret_Key_For_Dev_Only_Min_32_Chars!",
+ "Issuer": "quickapp-identity",
+ "Audience": "quickapp-api"
}
}
diff --git a/tests/notification-e2e-smoke.sh b/tests/notification-e2e-smoke.sh
new file mode 100755
index 0000000..5557e50
--- /dev/null
+++ b/tests/notification-e2e-smoke.sh
@@ -0,0 +1,139 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+# ─── Configuration ───────────────────────────────────────────────────────────
+DIRECT_URL="${DIRECT_URL:-http://localhost:5005}"
+GATEWAY_URL="${GATEWAY_URL:-http://localhost:5000}"
+FAILURES=0
+
+# ─── Colors ──────────────────────────────────────────────────────────────────
+GREEN='\033[0;32m'
+RED='\033[0;31m'
+NC='\033[0m' # No Color
+
+pass() { echo -e "${GREEN}[PASS]${NC} $1"; }
+fail() { echo -e "${RED}[FAIL]${NC} $1"; FAILURES=$((FAILURES + 1)); }
+
+# ─── Generate JWT ────────────────────────────────────────────────────────────
+JWT_SECRET='QuickApp_Microservices_SuperSecret_Key_For_Dev_Only_Min_32_Chars!'
+
+jwt_header=$(echo -n '{"alg":"HS256","typ":"JWT"}' | base64 -w0 | tr '+/' '-_' | tr -d '=')
+jwt_payload=$(echo -n '{"iss":"quickapp-identity","aud":"quickapp-api","sub":"test-user","exp":'$(date -d "+1 hour" +%s)'}' | base64 -w0 | tr '+/' '-_' | tr -d '=')
+jwt_signature=$(echo -n "${jwt_header}.${jwt_payload}" | openssl dgst -sha256 -hmac "${JWT_SECRET}" -binary | base64 -w0 | tr '+/' '-_' | tr -d '=')
+TOKEN="${jwt_header}.${jwt_payload}.${jwt_signature}"
+
+echo "======================================"
+echo " Notification Service E2E Smoke Tests"
+echo "======================================"
+echo ""
+echo "Direct URL: ${DIRECT_URL}"
+echo "Gateway URL: ${GATEWAY_URL}"
+echo ""
+
+# ─── Test 1: Health check ────────────────────────────────────────────────────
+echo "--- Test 1: GET /healthz (expect 200) ---"
+HTTP_CODE=$(curl -s -o /tmp/response_body -w '%{http_code}' "${DIRECT_URL}/healthz")
+if [ "$HTTP_CODE" = "200" ]; then
+ pass "GET /healthz returned ${HTTP_CODE}"
+else
+ fail "GET /healthz returned ${HTTP_CODE} (expected 200)"
+fi
+
+# ─── Test 2: Unauthenticated GET /api/notification ───────────────────────────
+echo "--- Test 2: GET /api/notification without auth (expect 401) ---"
+HTTP_CODE=$(curl -s -o /tmp/response_body -w '%{http_code}' "${DIRECT_URL}/api/notification")
+if [ "$HTTP_CODE" = "401" ]; then
+ pass "GET /api/notification without auth returned ${HTTP_CODE}"
+else
+ fail "GET /api/notification without auth returned ${HTTP_CODE} (expected 401)"
+fi
+
+# ─── Test 3: POST /api/notification/events/order-placed ──────────────────────
+echo "--- Test 3: POST /api/notification/events/order-placed with JWT (expect 201) ---"
+HTTP_CODE=$(curl -s -o /tmp/response_body -w '%{http_code}' \
+ -X POST \
+ -H "Authorization: Bearer ${TOKEN}" \
+ -H "Content-Type: application/json" \
+ -d '{
+ "orderId": "11111111-1111-1111-1111-111111111111",
+ "customerId": "22222222-2222-2222-2222-222222222222",
+ "totalAmount": 9999,
+ "placedAt": "2025-01-15T10:30:00Z"
+ }' \
+ "${DIRECT_URL}/api/notification/events/order-placed")
+
+if [ "$HTTP_CODE" = "201" ]; then
+ pass "POST /api/notification/events/order-placed returned ${HTTP_CODE}"
+else
+ fail "POST /api/notification/events/order-placed returned ${HTTP_CODE} (expected 201)"
+fi
+
+# Extract the notification ID from the response
+NOTIFICATION_ID=$(jq -r '.id' /tmp/response_body 2>/dev/null || echo "")
+if [ -z "$NOTIFICATION_ID" ] || [ "$NOTIFICATION_ID" = "null" ]; then
+ fail "Could not extract notification ID from POST response"
+ echo " Response body: $(cat /tmp/response_body)"
+fi
+
+# ─── Test 4: GET /api/notification with JWT ──────────────────────────────────
+echo "--- Test 4: GET /api/notification with JWT (expect 200, contains notification) ---"
+HTTP_CODE=$(curl -s -o /tmp/response_body -w '%{http_code}' \
+ -H "Authorization: Bearer ${TOKEN}" \
+ "${DIRECT_URL}/api/notification")
+
+if [ "$HTTP_CODE" = "200" ]; then
+ # Check that the response contains our notification ID
+ if echo "$(cat /tmp/response_body)" | jq -e ".[] | select(.id == \"${NOTIFICATION_ID}\")" > /dev/null 2>&1; then
+ pass "GET /api/notification returned ${HTTP_CODE} and contains created notification"
+ elif [ -z "$NOTIFICATION_ID" ] || [ "$NOTIFICATION_ID" = "null" ]; then
+ pass "GET /api/notification returned ${HTTP_CODE} (skipped body check — no ID from POST)"
+ else
+ fail "GET /api/notification returned ${HTTP_CODE} but response does not contain notification ${NOTIFICATION_ID}"
+ echo " Response body: $(cat /tmp/response_body | head -c 500)"
+ fi
+else
+ fail "GET /api/notification returned ${HTTP_CODE} (expected 200)"
+fi
+
+# ─── Test 5: GET /api/notification/{id}/preview ──────────────────────────────
+echo "--- Test 5: GET /api/notification/{id}/preview with JWT (expect 200, HTML) ---"
+if [ -n "$NOTIFICATION_ID" ] && [ "$NOTIFICATION_ID" != "null" ]; then
+ HTTP_CODE=$(curl -s -o /tmp/response_body -w '%{http_code}' \
+ -H "Authorization: Bearer ${TOKEN}" \
+ "${DIRECT_URL}/api/notification/${NOTIFICATION_ID}/preview")
+
+ if [ "$HTTP_CODE" = "200" ]; then
+ if grep -qi "