Files
baya-monorepo/server/src/Tests/Baya.Test.Api/PatientsApiTests.cs
T
hamid 39a979b1a7 @
backend phase 3: identity profiles, patients & nurse bank accounts

Add the role-attached identity layer on top of the b2 auth spine: nurse
seller profiles (guarded is_verified, read-only aggregates), thin customer
payer profiles, first-class patients (tenancy-scoped), and nurse payout bank
accounts hardened with an iban_hash uniqueness guard and an automated استعلام
شبا IBAN-ownership inquiry.

- Four usr tables via one migration (1:1 uniques, UNIQUE(iban_hash), filtered
  UNIQUE(nurse_id) WHERE is_primary=1, guarded is_verified, encrypted PII,
  soft-delete on nurse_profiles)
- 15 CQRS slices + 4 role-scoped controllers; reads projected + paginated,
  IBAN masked (last-4); ownership-inquiry endpoints rate-limited
- New IBankAccountOwnershipVerifier seam (mock deterministic شبا match) +
  per-domain repositories on IUnitOfWork + encrypted-PII value converters
- Activate FluentValidation repo-wide (validators were never registered)
- Handler unit tests + WebApplicationFactory integration tests (76 pass);
  contract identity-profiles.md + swagger snapshot; docs, handoff & report

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@
2026-07-02 12:03:15 +03:30

91 lines
3.8 KiB
C#

using System.Net;
using System.Net.Http.Json;
namespace Baya.Test.Api;
public class PatientsApiTests(BayaApiFactory factory) : IClassFixture<BayaApiFactory>
{
private static object PatientBody(string name, string gender = "female") => new
{
displayName = name,
firstName = "F",
lastName = "L",
birthDate = "1950-03-01",
gender,
bloodType = "O+",
initialMedicalNotes = "diabetic"
};
[Fact]
public async Task Create_List_Get_Update_Archive_Lifecycle()
{
var client = factory.CreateClient();
await ProfileTestClient.AuthenticateAsync(factory, client, "09123000001", "customer");
var create = await client.PostAsJsonAsync("/api/v1/patients/create", PatientBody("Mother"));
Assert.Equal(HttpStatusCode.OK, create.StatusCode);
var created = await AuthTestClient.ReadDataAsync(create);
var id = created.GetProperty("id").GetInt64();
Assert.Equal("female", created.GetProperty("gender").GetString());
Assert.Equal("diabetic", created.GetProperty("initialMedicalNotes").GetString());
var list = await client.GetAsync("/api/v1/patients/list");
var listData = await AuthTestClient.ReadDataAsync(list);
Assert.Equal(1, listData.GetProperty("total").GetInt32());
var get = await client.GetAsync($"/api/v1/patients/get/{id}");
Assert.Equal(HttpStatusCode.OK, get.StatusCode);
var update = await client.PostAsJsonAsync($"/api/v1/patients/update/{id}", PatientBody("Mother Renamed", "female"));
Assert.Equal(HttpStatusCode.OK, update.StatusCode);
var updated = await AuthTestClient.ReadDataAsync(update);
Assert.Equal("Mother Renamed", updated.GetProperty("displayName").GetString());
var archive = await client.PostAsJsonAsync($"/api/v1/patients/archive/{id}", new { });
Assert.Equal(HttpStatusCode.OK, archive.StatusCode);
var afterArchive = await client.GetAsync($"/api/v1/patients/get/{id}");
var archivedData = await AuthTestClient.ReadDataAsync(afterArchive);
Assert.False(archivedData.GetProperty("isActive").GetBoolean());
}
[Fact]
public async Task Get_And_Update_OfAnotherCustomersPatient_Return404()
{
// Customer A creates a patient.
var clientA = factory.CreateClient();
await ProfileTestClient.AuthenticateAsync(factory, clientA, "09123000002", "customer");
var create = await clientA.PostAsJsonAsync("/api/v1/patients/create", PatientBody("A's patient"));
var aPatientId = (await AuthTestClient.ReadDataAsync(create)).GetProperty("id").GetInt64();
// Customer B can neither read nor mutate A's patient — existence is not leaked.
var clientB = factory.CreateClient();
await ProfileTestClient.AuthenticateAsync(factory, clientB, "09123000003", "customer");
var get = await clientB.GetAsync($"/api/v1/patients/get/{aPatientId}");
Assert.Equal(HttpStatusCode.NotFound, get.StatusCode);
var update = await clientB.PostAsJsonAsync($"/api/v1/patients/update/{aPatientId}", PatientBody("hijack"));
Assert.Equal(HttpStatusCode.NotFound, update.StatusCode);
}
[Fact]
public async Task List_Unauthenticated_Returns401()
{
var client = factory.CreateClient();
var response = await client.GetAsync("/api/v1/patients/list");
Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
}
[Fact]
public async Task Create_MissingGender_Returns400()
{
var client = factory.CreateClient();
await ProfileTestClient.AuthenticateAsync(factory, client, "09123000004", "customer");
var response = await client.PostAsJsonAsync("/api/v1/patients/create", PatientBody("No gender", gender: ""));
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
}
}