4648
This commit is contained in:
@@ -1,38 +1,218 @@
|
||||
using FluentAssertions;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
using Xunit;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using FluentAssertions;
|
||||
|
||||
namespace raven_integration
|
||||
{
|
||||
|
||||
/*
|
||||
Make fresh data list tests in here, exercise sorting, filtering, crud, rights saved filters, whatever could break
|
||||
To save time instead of doing it the old way from before laboriously, just copy payloads from the client doing these things
|
||||
with fresh seeded data and ensure anything important doesn't break on refactor
|
||||
There could be some date issues from vague memory and note below so keeping that here for reference
|
||||
and keeping old tests until I implement this as a walkthrough to confirm if there is something I might have missed to add here
|
||||
DataList tests — three layers:
|
||||
|
||||
Just need to know datalists won't break on refactor, nothing fancy here but it was such a black hole of bugs in v7 I think I went overboard with the original
|
||||
integration tests but the v8 system is more bulletproof, likely don't need as many tests as I used during development.
|
||||
1. SavedFilterCrud (LIVE — runs now)
|
||||
Exercises POST/GET/PUT/DELETE on /data-list-filter plus the /list endpoint.
|
||||
Uses WorkOrderDataList as the list key since work orders always have seeded data.
|
||||
|
||||
2. FilterAndSort stubs (SKIPPED — need real payloads from browser)
|
||||
To fill these in:
|
||||
a) Open the AyaNova UI, navigate to the Work Orders grid
|
||||
b) Apply a filter (e.g. "Notes contains fox"), open browser DevTools -> Network
|
||||
c) Find the POST to /data-list — copy the full request body
|
||||
d) Use that body as the starting point for the test payload here
|
||||
See Util.BuildDataListRequestEx and Util.BuildSimpleFilterDataListViewColumn for helpers.
|
||||
|
||||
3. Rights stub (SKIPPED — needs a user with no list rights)
|
||||
Verify that a user without the WorkOrder list role gets 403.
|
||||
|
||||
------
|
||||
old potential BUGBUG: Server takes into account user's time zone offset when filtering lists by date range but here the local test runner just uses the windows system offset instead of the defined offset in the User account at the server
|
||||
Fix: Since seeder uses same time zone for all users it generates then can simply fetch one single users' tz offset and use that centerally to calculate a relative now and relative today
|
||||
same as the server does but in a central location here for all tests to use.
|
||||
|
||||
*/
|
||||
Date filter note: Server accounts for user's time zone offset when filtering by
|
||||
date range. All seeded users share the same tz offset (TIME_ZONE_ADJUSTMENT in util.cs).
|
||||
Use ToOffsetAdjustedUniversalTime() extension when building date filter values.
|
||||
*/
|
||||
|
||||
public class DataListOperations
|
||||
{
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// 1. SAVED FILTER CRUD
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
[Fact(Skip = "TODO: Implement validation test for DataLists")]
|
||||
public void DataLists_ShouldSortFilterAllTypesAndSaveUpdate()
|
||||
/// <summary>
|
||||
/// Create, retrieve, list, update, and delete a saved filter.
|
||||
/// Also verifies public/private visibility rules:
|
||||
/// - a public filter IS visible to other users
|
||||
/// - a private filter is NOT visible to other users
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task SavedFilterCRUD()
|
||||
{
|
||||
var token = await Util.GetTokenAsync("BizAdmin");
|
||||
const string ListKey = "WorkOrderDataList";
|
||||
|
||||
// CREATE a public saved filter
|
||||
// UserId=0 — the server sets the actual user id from the auth token
|
||||
var filterName = Util.Uniquify("Test Saved Filter");
|
||||
var payload = $$"""
|
||||
{"id":0,"concurrency":0,"userId":0,"name":"{{filterName}}","public":true,"defaultFilter":false,"listKey":"{{ListKey}}","filter":null}
|
||||
""";
|
||||
|
||||
ApiResponse a = await Util.PostAsync("data-list-filter", token, payload);
|
||||
Util.ValidateDataReturnResponseOk(a);
|
||||
long Id = a.ObjectResponse["data"]["id"].Value<long>();
|
||||
Id.Should().BeGreaterThan(0);
|
||||
a.ObjectResponse["data"]["name"].Value<string>().Should().Be(filterName);
|
||||
a.ObjectResponse["data"]["public"].Value<bool>().Should().BeTrue();
|
||||
|
||||
// GET by id
|
||||
a = await Util.GetAsync($"data-list-filter/{Id}", token);
|
||||
Util.ValidateDataReturnResponseOk(a);
|
||||
a.ObjectResponse["data"]["name"].Value<string>().Should().Be(filterName);
|
||||
a.ObjectResponse["data"]["listKey"].Value<string>().Should().Be(ListKey);
|
||||
var concurrency = a.ObjectResponse["data"]["concurrency"].Value<uint>();
|
||||
|
||||
// A different user should see a PUBLIC filter
|
||||
var otherToken = await Util.GetTokenAsync("SubContractorRestricted");
|
||||
a = await Util.GetAsync($"data-list-filter/{Id}", otherToken);
|
||||
Util.ValidateDataReturnResponseOk(a);
|
||||
a.ObjectResponse["data"]["name"].Value<string>().Should().Be(filterName);
|
||||
|
||||
// LIST — filter should appear in the list for its key
|
||||
a = await Util.GetAsync($"data-list-filter/list?ListKey={ListKey}", token);
|
||||
Util.ValidateDataReturnResponseOk(a);
|
||||
((JArray)a.ObjectResponse["data"]).Should().Contain(
|
||||
z => z["id"].Value<long>() == Id,
|
||||
"the saved filter should appear in the list for its key");
|
||||
|
||||
// PUT — make it private and rename
|
||||
var updatedName = Util.Uniquify("Updated Saved Filter");
|
||||
var putPayload = $$"""
|
||||
{"id":{{Id}},"concurrency":{{concurrency}},"userId":0,"name":"{{updatedName}}","public":false,"defaultFilter":false,"listKey":"{{ListKey}}","filter":null}
|
||||
""";
|
||||
a = await Util.PutAsync("data-list-filter", token, putPayload);
|
||||
Util.ValidateHTTPStatusCode(a, 200);
|
||||
|
||||
// Verify the update
|
||||
a = await Util.GetAsync($"data-list-filter/{Id}", token);
|
||||
Util.ValidateDataReturnResponseOk(a);
|
||||
a.ObjectResponse["data"]["name"].Value<string>().Should().Be(updatedName);
|
||||
a.ObjectResponse["data"]["public"].Value<bool>().Should().BeFalse();
|
||||
var newConcurrency = a.ObjectResponse["data"]["concurrency"].Value<uint>();
|
||||
newConcurrency.Should().NotBe(concurrency, "concurrency should have been bumped");
|
||||
|
||||
// The other user should NO LONGER see the now-private filter
|
||||
a = await Util.GetAsync($"data-list-filter/{Id}", otherToken);
|
||||
Util.ValidateResponseNotFound(a);
|
||||
|
||||
// DELETE
|
||||
a = await Util.DeleteAsync($"data-list-filter/{Id}", token);
|
||||
Util.ValidateHTTPStatusCode(a, 204);
|
||||
|
||||
// Confirm deleted
|
||||
a = await Util.GetAsync($"data-list-filter/{Id}", token);
|
||||
Util.ValidateResponseNotFound(a);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A saved filter with DefaultFilter=true must be rejected — only the server
|
||||
/// can create default filters internally.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task SavedFilter_DefaultFilterCannotBeCreatedByClient()
|
||||
{
|
||||
var token = await Util.GetTokenAsync("BizAdmin");
|
||||
var payload = """
|
||||
{"id":0,"concurrency":0,"userId":0,"name":"Should Fail","public":false,"defaultFilter":true,"listKey":"WorkOrderDataList","filter":null}
|
||||
""";
|
||||
ApiResponse a = await Util.PostAsync("data-list-filter", token, payload);
|
||||
Util.ValidateHTTPStatusCode(a, 400);
|
||||
a.ObjectResponse["error"].Should().NotBeNull();
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// 2. FILTER AND SORT — stubs awaiting real payloads from the browser
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
// How to capture a real payload:
|
||||
// 1. Run the server locally (dotnet run from raven/server/AyaNova/)
|
||||
// 2. Open the UI, navigate to Work Orders grid
|
||||
// 3. Apply a filter through the UI (e.g. Notes "contains" something)
|
||||
// 4. DevTools > Network — find the POST to /api/v8/data-list
|
||||
// 5. Copy the full request body JSON
|
||||
// 6. Use Util.BuildDataListRequestEx() or paste directly as a raw string
|
||||
//
|
||||
// Field keys for WorkOrderDataList (see WorkOrderDataList.cs in server):
|
||||
// "WorkOrderSerialNumber" integer (is the row id field)
|
||||
// "Customer" string
|
||||
// "WorkOrderServiceDate" datetime
|
||||
// "WorkOrderCloseByDate" datetime
|
||||
// "WorkOrderStatus" pick list / string
|
||||
// "Project" string
|
||||
// "WorkOrderAge" integer
|
||||
//
|
||||
// Operator constants are on Util: OpContains, OpStartsWith, OpEndsWith,
|
||||
// OpEquality, OpNotEqual, OpGreaterThan, OpLessThan, etc.
|
||||
//
|
||||
// Sort direction string: "+" ascending, "-" descending
|
||||
//
|
||||
// NOTE on date filters: use DateTime.Now.ToOffsetAdjustedUniversalTime()
|
||||
// (extension in util.cs) to avoid DST/timezone mismatch failures.
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
[Fact(Skip = "TODO: Capture real list payload from browser — see comment above")]
|
||||
public void WorkOrderList_StringContainsFilterWorks()
|
||||
{
|
||||
// Example shape once confirmed via browser:
|
||||
//
|
||||
// var token = await Util.GetTokenAsync("BizAdmin");
|
||||
// dynamic dListView = new JArray();
|
||||
// dListView.Add(Util.BuildSimpleFilterDataListViewColumn("Customer", Util.OpContains, "Acme"));
|
||||
// string body = Util.BuildDataListRequestEx(dListView, limit: 50, offset: 0, dataListKey: "WorkOrderDataList");
|
||||
// ApiResponse a = await Util.PostAsync("data-list", token, body);
|
||||
// Util.ValidateDataReturnResponseOk(a);
|
||||
// var rows = (JArray)a.ObjectResponse["data"];
|
||||
// rows.Should().NotBeEmpty();
|
||||
// // each row should have "Acme" somewhere in the Customer column value
|
||||
}
|
||||
|
||||
[Fact(Skip = "TODO: Capture real list payload from browser")]
|
||||
public void WorkOrderList_StringStartsWithFilterWorks() { }
|
||||
|
||||
[Fact(Skip = "TODO: Capture real list payload from browser")]
|
||||
public void WorkOrderList_StringEqualsFilterWorks() { }
|
||||
|
||||
[Fact(Skip = "TODO: Capture real list payload from browser")]
|
||||
public void WorkOrderList_StringNotEqualFilterWorks() { }
|
||||
|
||||
[Fact(Skip = "TODO: Capture real list payload from browser")]
|
||||
public void WorkOrderList_DateRangeFilterWorks() { }
|
||||
|
||||
[Fact(Skip = "TODO: Capture real list payload from browser")]
|
||||
public void WorkOrderList_NullFieldFilterWorks() { }
|
||||
|
||||
[Fact(Skip = "TODO: Capture real list payload from browser")]
|
||||
public void WorkOrderList_MultiConditionAndFilterWorks() { }
|
||||
|
||||
[Fact(Skip = "TODO: Capture real list payload from browser")]
|
||||
public void WorkOrderList_SortAscendingWorks() { }
|
||||
|
||||
[Fact(Skip = "TODO: Capture real list payload from browser")]
|
||||
public void WorkOrderList_SortDescendingWorks() { }
|
||||
|
||||
[Fact(Skip = "TODO: Capture real list payload from browser")]
|
||||
public void WorkOrderList_PaginationOffsetAndLimitWork() { }
|
||||
|
||||
[Fact(Skip = "TODO: Capture real list payload from browser")]
|
||||
public void WorkOrderList_ReturnFormatMatchesExpectedShape() { }
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// 3. RIGHTS
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
[Fact(Skip = "TODO: Identify a seeded user with no WorkOrder list rights and use their login")]
|
||||
public void WorkOrderList_UserWithoutListRightsGets403() { }
|
||||
|
||||
}//eoc
|
||||
}//eons
|
||||
|
||||
Reference in New Issue
Block a user