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
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ jobs:
run: dotnet build holonsoft.AutoPoco.slnx -c Release --no-restore

- name: Test
run: dotnet test holonsoft.AutoPoco.Tests/holonsoft.AutoPoco.Tests.csproj -c Release --no-build
run: dotnet test holonsoft.AutoPoco.slnx -c Release --no-build

- name: Pack
run: dotnet pack holonsoft.AutoPoco.slnx -c Release --no-build -o artifacts
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ jobs:
run: dotnet build holonsoft.AutoPoco.slnx -c Release

- name: Test
run: dotnet test holonsoft.AutoPoco.Tests/holonsoft.AutoPoco.Tests.csproj -c Release --no-build
run: dotnet test holonsoft.AutoPoco.slnx -c Release --no-build

- name: Pack
run: dotnet pack holonsoft.AutoPoco.slnx -c Release --no-build -o artifacts
Expand All @@ -46,7 +46,9 @@ jobs:
run: |
expected="${GITHUB_REF_NAME#v}"
ls -l artifacts
# every package that the push step uploads has to carry the version of the tag
test -f "artifacts/holonsoft.AutoPoco.${expected}.nupkg"
test -f "artifacts/holonsoft.AutoPoco.Faker.${expected}.nupkg"

- name: NuGet login (OIDC)
id: login
Expand Down
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
<RepositoryType>git</RepositoryType>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageTags>autopoco;testdata;test-data;poco;generator;fluent;fake-data</PackageTags>
<PackageReleaseNotes>6.0: Lambda data sources (FuncSource, Setup(...).From(() => ...), Source(expr, () => ...) at generation time). Opt-in nullability-aware null generation via RespectNullableAnnotations(threshold). Generic NumberSource&lt;T&gt; and NullableNumberSource&lt;T&gt; with min/max for every built-in numeric type (integers uniform and inclusive, floating point and decimal overflow safe). Records and immutable types are constructed through their constructor, parameters are fed from the registered members with the same name, no parameterless constructor needed anymore; unconfigured struct parameters stay default instead of throwing. Seeds: UseSeed(seed) per factory, CreateSession(recursionLimit, seed) per session, every member gets its own stream derived from the seed. Own deterministic generator (StableRandom, xoshiro256**) instead of System.Random, sequences are stable across .NET versions. Breaking: AutoPocoGlobalSettings removed (AutoPocoDefaults constants instead), every generated sequence changed once, the max of IntegerSource/LongSource/RandomNumberSource and their nullable variants is inclusive now (it was exclusive), Int128Source/NullableInt128Source pick uniformly instead of clamping to the bounds (they never produced a negative value before), RandomStringSource and UrlSource reach the last character resp. the last top level domain, ColorSource/RandomTextSource/RandomUtfTextSource treat their maximum as inclusive, ColorSource applies the alpha of SetColorRange, and UrlSource and ExtendedEmailAddressSource seed their nested sources (the URL host ignored the session seed completely). CreditCardSource never produced a Discover card. TimeSpanSource was rewritten: it returned TimeSpan.MinValue on every draw of the default range, produced values outside a requested range and looped forever on a reversed one. DoubleSource returned infinity over its default range, DoubleSource/FloatSource/DecimalSource interpolate overflow safe now and DecimalSource keeps its full precision. Index-aware Impose on lists and selections (value from the position, or from position and item), Impose(member, item => value) on a single generator. Invoke arguments: Use.From(lambda), constants and variables arrive as values. Context lambdas get a non-nullable IGenerationContext. Argument validation over the public API, meaningful exception messages, XML documentation file. IDataSource&lt;T&gt; is covariant. Bug fixes: Nullable* sources honor an explicit null creation threshold (null positions of stable sequences changed), DateTime/DateOnly/TimeOnly/DateOfBirth sources cover their whole range including the upper bound (sequences changed), RandomUtfTextSource no longer loops forever. Build and packaging modernized: GitHub Actions CI, trusted publishing to nuget.org, MinVer tag based versioning, central package management, xunit.v3 tests on net8/9/10. FluentAssertions and Moq are no longer dependencies of the library. See README for details.</PackageReleaseNotes>
<PackageReleaseNotes>6.1: New data sources for trade identifiers in holonsoft.AutoPoco.DataSources.Identifiers: GtinSource and Ean13Source (GTIN-8/12/13/14, i.e. EAN-8, UPC-A, EAN-13 and ITF-14, with a valid GS1 mod 10 check digit and an optional GS1 company prefix), SsccSource (the eighteen digit number of a logistic unit, same GS1 check digit, optional prefix), IsbnSource (ISBN-13 inside an allocated registration group, ISBN-10 with its mod 11 check digit including the X), IssnSource (mod 11 check character including the X), IsinSource (a real country prefix or a given one, Luhn over the expanded letters), ImeiSource (Luhn check digit, optional Type Allocation Code prefix) and AsinSource, each with a Nullable variant. TestBinCreditCardSource in holonsoft.AutoPoco.DataSources.Business builds card numbers on the six digit test BINs the payment processors publish, so the data is recognisable as test data. Bug fixes: CreditCardSource doubled the wrong digits when calculating the Luhn check digit, so its numbers failed a real Luhn validation; and only the first digit of the scheme was fixed, so an "American Express" could be detected as a Diners Club and a "MasterCard" could start with 56, a range no scheme issues. Every number now carries a correct check digit and starts inside a range its scheme really issues in (34/37 for American Express, 51-55 and 2221-2720 for Mastercard, 6011/644-649/65 for Discover), and the generated sequences changed once for that. 6.0: Lambda data sources (FuncSource, Setup(...).From(() => ...), Source(expr, () => ...) at generation time). Opt-in nullability-aware null generation via RespectNullableAnnotations(threshold). Generic NumberSource&lt;T&gt; and NullableNumberSource&lt;T&gt; with min/max for every built-in numeric type (integers uniform and inclusive, floating point and decimal overflow safe). Records and immutable types are constructed through their constructor, parameters are fed from the registered members with the same name, no parameterless constructor needed anymore; unconfigured struct parameters stay default instead of throwing. Seeds: UseSeed(seed) per factory, CreateSession(recursionLimit, seed) per session, every member gets its own stream derived from the seed. Own deterministic generator (StableRandom, xoshiro256**) instead of System.Random, sequences are stable across .NET versions. Breaking: AutoPocoGlobalSettings removed (AutoPocoDefaults constants instead), every generated sequence changed once, the max of IntegerSource/LongSource/RandomNumberSource and their nullable variants is inclusive now (it was exclusive), Int128Source/NullableInt128Source pick uniformly instead of clamping to the bounds (they never produced a negative value before), RandomStringSource and UrlSource reach the last character resp. the last top level domain, ColorSource/RandomTextSource/RandomUtfTextSource treat their maximum as inclusive, ColorSource applies the alpha of SetColorRange, and UrlSource and ExtendedEmailAddressSource seed their nested sources (the URL host ignored the session seed completely). CreditCardSource never produced a Discover card. TimeSpanSource was rewritten: it returned TimeSpan.MinValue on every draw of the default range, produced values outside a requested range and looped forever on a reversed one. DoubleSource returned infinity over its default range, DoubleSource/FloatSource/DecimalSource interpolate overflow safe now and DecimalSource keeps its full precision. Index-aware Impose on lists and selections (value from the position, or from position and item), Impose(member, item => value) on a single generator. Invoke arguments: Use.From(lambda), constants and variables arrive as values. Context lambdas get a non-nullable IGenerationContext. Argument validation over the public API, meaningful exception messages, XML documentation file. IDataSource&lt;T&gt; is covariant. Bug fixes: Nullable* sources honor an explicit null creation threshold (null positions of stable sequences changed), DateTime/DateOnly/TimeOnly/DateOfBirth sources cover their whole range including the upper bound (sequences changed), RandomUtfTextSource no longer loops forever. Build and packaging modernized: GitHub Actions CI, trusted publishing to nuget.org, MinVer tag based versioning, central package management, xunit.v3 tests on net8/9/10. FluentAssertions and Moq are no longer dependencies of the library. See README for details.</PackageReleaseNotes>
</PropertyGroup>

<PropertyGroup Label="Package build">
Expand Down
6 changes: 6 additions & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@
<PackageVersion Include="MinVer" Version="7.0.0" />
</ItemGroup>

<ItemGroup Label="Faker integration">
<!-- Exact version on purpose, the brackets pin it. Bogus draws its values from locale data that ships
inside the package, so a different Bogus version produces different values for the same seed. -->
<PackageVersion Include="Bogus" Version="[35.6.5]" />
</ItemGroup>

<ItemGroup Label="Test">
<PackageVersion Include="Shouldly" Version="4.3.0" />
<PackageVersion Include="Moq" Version="4.20.72" />
Expand Down
76 changes: 76 additions & 0 deletions README.Faker.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# holonsoft.AutoPoco.Faker

Realistic values for [AutoPoco](https://github.com/holonsoft/AutoPoco), taken from [Bogus](https://github.com/bchavez/Bogus).

```
dotnet add package holonsoft.AutoPoco.Faker
```

The split of work is the whole idea. Bogus is good at a single believable value, AutoPoco is good at everything around it: building the object graph, keeping members apart, following relationships and repeating the whole thing for a seed. Bogus never builds an object here, it only answers "give me a city name".

## The sources

| Source | Produces |
| --- | --- |
| `FakerFirstNameSource`, `FakerLastNameSource`, `FakerFullNameSource` | Names in the language of the locale |
| `FakerEmailAddressSource` | An email address on one of the example domains |
| `FakerPhoneNumberSource` | A phone number in the format of the locale |
| `FakerStreetAddressSource`, `FakerCitySource`, `FakerPostalCodeSource`, `FakerCountrySource` | The parts of an address |
| `FakerCompanyNameSource` | A company name including its legal form, e.g. "Müller GmbH" |
| `FakerProductNameSource` | A product name that reads like a catalogue entry |
| `FakerPriceSource` | A `decimal` amount between two bounds, 1 to 1000 with 2 decimals by default |
| `FakerCurrencyCodeSource` | An ISO 4217 code such as `EUR` |
| `FakerDateTimeSource` | A point in time between two bounds, 2000-01-01 to 2035-12-31 by default |

Every one of them has a `Nullable...` variant, like the sources of the core package.

```csharp
var factory = AutoPocoContainer.Configure(x => {
x.UseSeed(4711);

x.Include<Customer>()
.Setup(c => c.FirstName).Use<FakerFirstNameSource>("de")
.Setup(c => c.LastName).Use<FakerLastNameSource>("de")
.Setup(c => c.EmailAddress).Use<FakerEmailAddressSource>("de")
.Setup(c => c.City).Use<FakerCitySource>("de");
});

// the same customers, every run, in every process
var customers = factory.CreateSession().Collection<Customer>(1000);
```

The first argument is the Bogus locale, written lower case with an underscore before the region: `en`, `en_US`, `de`, `de_AT`, `de_CH` and so on. Leave it out for the default `en`. Every locale of the pinned Bogus version works with every source, and where a locale misses a piece of data Bogus falls back to English rather than failing, so an unusual locale can give you an English city.

## Seeds and the limits of the guarantee

The AutoPoco session seed drives Bogus. Every source instance owns one Bogus faker, that faker draws from AutoPoco's own `StableRandom`, and the static `Bogus.Randomizer.Seed` is never read or written. So the same seed gives the same data and each member keeps its own stream.

One limit worth knowing. AutoPoco guarantees a sequence across .NET versions because it brings its own generator. Bogus takes its values from locale data that ships inside its package, so a different Bogus version can give different names for the same seed. This package therefore depends on **exactly** Bogus 35.6.5, not "35.6.5 or newer". If your project already references a different Bogus version you get a NuGet version conflict instead of a silent change of your test data, which is the trade this package makes on purpose. Pin this package too when your tests compare against stored data.

## What stays in the core package

Numbers with a check digit. Bogus has a `Commerce.Ean` of its own whose check digit is not guaranteed to be valid, so use `Ean13Source`, `GtinSource`, `IsbnSource` and `AsinSource` from `holonsoft.AutoPoco` for article numbers and let Bogus do the names.

The sources are also independent of each other. A `FakerCitySource` and a `FakerPostalCodeSource` on the same object give a correct city and a correct postal code, but not a postal code that belongs to that city, and an email address does not contain the name of the customer it sits on. Build the member from the finished object where the parts have to match:

```csharp
var customers = factory.CreateSession().Collection<Customer>(1000, c =>
c.Impose(x => x.EmailAddress, (_, x) => $"{x.FirstName}.{x.LastName}@example.com".ToLowerInvariant()));
```

## The two packages together

```csharp
x.Include<Product>()
.Setup(p => p.Name).Use<FakerProductNameSource>("de") // Bogus, believable
.Setup(p => p.Ean).Use<Ean13Source>("40063") // core, check digit is correct
.Setup(p => p.Price).Use<FakerPriceSource>(5m, 500m, 2, "de");

x.Include<Order>()
.Setup(o => o.Customer).Use<AutoSource<Customer>>() // AutoPoco, the graph
.Setup(o => o.Lines).Collection(1, 4);
```

Bogus creates believable single values, AutoPoco creates the consistent, repeatable object graph around them. That combination is what fills a shared developer database.

Full documentation of AutoPoco itself: https://github.com/holonsoft/AutoPoco
Loading
Loading