Authoritative improvement backlog for the redcap-api .NET 10 library. Items are ordered highest to lowest priority. Every item is directly actionable: a developer should be able to pick it up, know exactly which file(s) to change, and understand why.
Priority bands:
- P1 — Blocking / must be done before any other work lands on
master - P2 — Important / high-value quality improvements
- P3 — Nice-to-have / developer-experience polish
- P4 — Future / research-stage, no immediate urgency
- Merge
newtonsoft-json-migration→masterAll 216 tests pass. Nothing else should land untilmasterreflects the current working state. Rungit checkout master && git merge --no-ff newtonsoft-json-migration, verifydotnet teststill passes, then delete the feature branch.
-
Remove the "CI" section from
CLAUDE.mdCLAUDE.mdreferences.github/workflows/ci.yml,.github/workflows/publish-github-packages.yml,bitbucket-pipelines.yml,azure-pipelines.yml, and.travis.yml. None of these files exist or will ever exist in this repo. Delete the entire "CI" section to eliminate confusion for contributors. -
Fix the dependency description in
CLAUDE.mdThe "Repository Layout" section currently reads "The library depends only on Newtonsoft.Json and Serilog." The migration is complete; the library no longer references Newtonsoft.Json. Update to: "The library depends only on Serilog; JSON serialization usesSystem.Text.Jsonfrom the .NET runtime." -
Remove stale CI badge references from
README.mdThe README header contains Azure Pipelines and Travis CI badgeimglinks that reference non-existent pipelines. Remove them entirely. -
Remove the
Newtonsoft.Jsonversion pin fromDirectory.Packages.props<PackageVersion Include="Newtonsoft.Json" Version="13.0.4" />remains in the central package manifest even though no project references it after the migration. Delete the entry.
- Create
CHANGELOG.mdwith a v1.x → v2.x migration guide Users upgrading have no consolidated reference for breaking changes. CreateCHANGELOG.mdat the repo root (use Keep a Changelog format) with a## [2.0.0]section covering: token moved to constructor;IRedcapTransportinjection pattern;DefaultRedcapTransportlifetime andFromHttpClient;BrokenCertificatereplacing the old staticUtils.UseInsecureCertificateflag;System.Text.Jsonreplacing Newtonsoft.Json.
-
Simplify
ConvertArraytoString<T>()insrc/RedcapApi/Utilities/Utils.csThe method uses a manualStringBuilderloop that appends a trailing comma on every element and callsTrimEnd(','). Replace the entire loop body withreturn string.Join(",", inputArray);. Existing tests inUtilitiesTests.cscover the behavior; verify they still pass. -
Cache reflection results in
GetDisplayName(this Enum)insrc/RedcapApi/Utilities/Utils.csGetDisplayNamecallsGetType().GetMember(...)andGetCustomAttribute<DisplayAttribute>()on every invocation — once per payload key per API call. Introduce astatic readonly ConcurrentDictionary<(Type, string), string> _displayNameCacheand populate it on first access for each(type, memberName)pair. Public signature is unchanged; no test changes required. -
Introduce a
PayloadKeystatic constants class Literal strings such as"content","format","action","token","records","returnFormat","data", and"type"appear as dictionary keys throughoutsrc/RedcapApi/Api/RedcapApi.csand its partial files. Createsrc/RedcapApi/Api/PayloadKey.cswithpublic static class PayloadKeyholdingpublic const string Content = "content";etc. Replace all raw strings inAddContent,AddFormattedRequest,AddActionRequest,AddImportRequest,CreatePayload, and related helpers. -
Add
RecordExportOptions/RecordImportOptionsparameter objectsExportRecordsAsyncinsrc/RedcapApi/Api/RedcapApi.Records.cshas 21 parameters. Add non-breaking overloadsExportRecordsAsync(RecordExportOptions options, CancellationToken cancellationToken = default)and the equivalent for import, where the options objects carry all current optional parameters as properties with identical defaults. Keep all existing overloads intact — this is purely additive. Document in CONTRIBUTING.md that new endpoints with more than ~8 optional parameters should prefer options objects.
-
Extract
TestConstantsfrom test files The string"token123"is declared asprivate const string Tokenindependently in every test class. Createtests/RedcapApi.Tests/TestConstants.cswithinternal static class TestConstants { public const string Token = "token123"; ... }and replace all duplicate declarations. Add any other repeated literals (localhost base URL, common record IDs) as named constants here. -
Convert copy-paste overload tests to
[Theory]+[InlineData]RedcapApiTransportTests.cshas 152[Fact]methods and zero[Theory]usages. Many tests for overload variants differ only in which optional arguments are omitted. Identify clusters of structurally identical tests (e.g., multipleExportArmsAsyncoverloads that each assertcontent=arm) and consolidate them into[Theory]+[InlineData]or[MemberData]. Target ≥20% reduction in total[Fact]count without losing any assertion coverage. -
Add cancellation forwarding tests for all domain areas
CancellationTests.csverifiesCancellationTokenforwarding for Records, Files, and Users only. Every remaining domain (Arms, DAGs, Events, FieldNames, FileRepository, Instruments, Logging, Metadata, Projects, RepeatingInstruments, Reports, Surveys, UserRoles, Version) needs at least one_ForwardsCancellationTokenToTransporttest. The pattern already exists inCancellationTests.cs. -
Add
ConcurrencyTests.csThere are no tests that verify the transport and API instance are safe under concurrent load. Add a test class that issues 20–50 parallel calls against aLocalHttpServerusing a single sharedRedcapApiinstance and asserts all calls complete without exceptions or data mixing. This documents — and guards — the thread-safety invariant ofDefaultRedcapTransport. -
Extend
FakeTransportwithAllPayloadshistoryFakeTransport(bottom ofRedcapApiTransportTests.cs) only storesLastDictionaryPayload/LastMultipartPayload/LastDownloadDestinationPath— the most recent call only. Multi-step workflows that call multiple API methods sequentially cannot assert on earlier calls. Addpublic List<Dictionary<string, string>> AllDictionaryPayloads { get; } = new();andpublic List<MultipartFormDataContent> AllMultipartPayloads { get; } = new();, appending to them on each call alongside updating theLast*properties. -
Create
ValidationTests.csfor guard-clause coverage There is no dedicated test class for input-validation behavior. Createtests/RedcapApi.Tests/ValidationTests.csand add tests verifying:ArgumentNullExceptionfrom the constructor whentokenis null;ArgumentExceptionwhentokenis empty;RedcapApiExceptionfromRequireItems(...)when a required collection is empty (e.g.,DeleteArmsAsync(Array.Empty<RedcapArm>())). UseFakeTransportand assertLastDictionaryPayload == nullto confirm the transport is never invoked. -
Add 429 and 503 error simulation in
HttpErrorTests.csHttpErrorTests.cstests only 401, 403, 400, and 500. Add[InlineData(429)]and[InlineData(503)]to the existing[Theory]so those status codes are verified to produceRedcapApiException. Also add a test for HTTP 200 with a malformed JSON body that verifies typed overloads (e.g.,ExportDagsTypedAsync) throwRedcapApiExceptionwith a meaningful message rather than an unhandledJsonException.
-
Add
.editorconfigat the repo root Without one, C# formatting rules are IDE-dependent with no tooling enforcement. Create.editorconfigwith at minimum:indent_style = space,indent_size = 4,end_of_line = lf,charset = utf-8-bom, and the standard[*.cs]C# analyzer severity settings that align withTreatWarningsAsErrors = truealready inDirectory.Build.props. -
Add
global.jsonto pin the .NET 10 SDK version Different team members may use different .NET 10 preview/stable SDK builds. Addglobal.jsonat the repo root with{ "sdk": { "version": "10.0.xxx", "rollForward": "latestPatch" } }wherexxxmatches the team's current toolchain (dotnet --version). This prevents build surprises when a new preview SDK ships. -
Add
Microsoft.CodeAnalysis.NetAnalyzersThe repo enforcesTreatWarningsAsErrorsbut has no Roslyn analyzer package beyond the compiler. Add<PackageVersion Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="9.0.0" />toDirectory.Packages.propsand<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" PrivateAssets="all" />to a shared<ItemGroup>inDirectory.Build.props. Set baseline severities in.editorconfig. -
Add
build.ps1for one-command dev setup Contributors currently assembledotnetcommands from CONTRIBUTING.md manually. Addbuild.ps1at the repo root with named targets:Restore,Build,Test,TestE2E. A./build.ps1 -Target Testinvocation becomes the canonical "does everything pass?" command.
-
Clean up or remove
bitbucket-pipelines.ymlCLAUDE.md notes the pipeline block is duplicated (only the first copy runs). Since this repo has no CI, either delete the file entirely or leave exactly one block with a comment. Do not let the duplication silently grow. -
Document the "no CI / no automated publish" decision in
CONTRIBUTING.mdAdd a short note so future contributors do not add CI pipelines or NuGet publish workflows. Saves future sessions from rediscovering the decision.
-
Stryker.NET mutation testing Run Stryker.NET against
src/RedcapApito measure mutation score forUtils.cs,DefaultRedcapTransport.cs, and the Execute helpers inRedcapApi.cs. A score below 70% in core logic suggests the test suite has blind spots that[Theory]consolidation alone will not catch. Addstryker-config.jsonand a./build.ps1 -Target Mutatetarget when approved. Runs locally — no CI required. Current baseline:65.35%mutation score. -
DocFX API documentation site The library has thorough XML doc comments on all public surfaces. Add a
docs/folder with adocfx.jsontargetingsrc/RedcapApiand instructions in CONTRIBUTING.md for generating the site locally (docfx docs/docfx.json --serve). -
BenchmarkDotNet performance baselines Add a
benchmarks/RedcapApi.Benchmarksproject measuring:GetDisplayNamewith and without the reflection cache;ConvertArraytoString<T>for various array sizes;JsonSerializer.Serialize/Deserializefor representative model payloads. Establish baselines before any performance-motivated refactoring. -
ArchUnitNET architectural constraint tests Add an
ArchitectureTests.cstest class enforcing: classes inRedcap.Apimust not reference the test project;DefaultRedcapTransportmust only be instantiated through its constructors orFromHttpClient;Utils.csmust not have cyclic references back intoRedcap.Api. Record current violations as known exceptions until resolved.
- Expand the E2E test suite
Only one E2E test exists (
ExportRecordAsync). Add[SkippableFact]tests — guarded by the sameREDCAP_E2E_*env-var pattern and[Trait("Category", "E2E")]attribute — for:ImportRecordsAsync,ExportUsersAsync/ExportUsersTypedAsync, anImportFileAsync+ExportFileAsyncround-trip, andExportSurveyLinkAsync.