Skip to content

Commit

Permalink
Added extension Samordningsnummer with unit tests (#414)
Browse files Browse the repository at this point in the history
* Added extension Samordningsnummer with unit tests

* Add Samordningsnummer API doc

---------

Co-authored-by: Brian Chavez <bchavez@bitarmory.com>
  • Loading branch information
aschan and bchavez authored Jan 14, 2024
1 parent ede0e69 commit 78d25fc
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 9 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,7 @@ In the examples above, all three alternative styles of using **Bogus** produce t
* `Bogus.Person.Cnp()` - Romanian Personal Identification number (CNP)
* **`using Bogus.Extensions.Sweden;`**
* `Bogus.Person.Personnummer()` - Swedish national identity number
* `Bogus.Person.Samordningsnummer()` - Swedish coordination number
* **`using Bogus.Extensions.UnitedKingdom;`**
* `Bogus.DataSets.Vehicle.GbRegistrationPlate()` - GB Vehicle Registration Plate
* `Bogus.DataSets.Finance.SortCode()` - Banking Sort Code
Expand Down
60 changes: 53 additions & 7 deletions Source/Bogus.Tests/ExtensionTests/SwedishExtensionTest.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System;
using System.Linq;
using Bogus.DataSets;
using Bogus.Extensions.Sweden;
Expand All @@ -20,33 +21,73 @@ public void can_create_valid_swedish_personnummer()
}

[Fact]
public void when_person_is_male_second_last_number_is_even()
public void can_create_valid_swedish_samordningsnummer()
{
var f = new Faker("sv");
var person = f.Person;

var samordningsnummer = person.Samordningsnummer();

CheckLuhn(samordningsnummer.Substring(2)).Should().BeTrue();
}

[Fact]
public void personnummer_should_contain_valid_date_of_birth()
{
var f = new Faker("sv");
var person = f.Person;
person.Gender = Name.Gender.Male;

var personnummer = person.Personnummer();
var (year, month, day) = ExtractDateParts(personnummer);
var dateOfBirth = new DateTime(year, month, day);

dateOfBirth.Date.Should().Be(person.DateOfBirth.Date);
}

var secondLast = int.Parse(personnummer.Substring(personnummer.Length - 2, 1));
[Fact]
public void samordningsnummer_should_contain_offset_date_of_birth()
{
var f = new Faker("sv");
var person = f.Person;

var samordningsnummer = person.Samordningsnummer();
var (year, month, day) = ExtractDateParts(samordningsnummer);
var dateOfBirth = new DateTime(year, month, day - 60);

dateOfBirth.Date.Should().Be(person.DateOfBirth.Date);
}

[Theory]
[InlineData(false)]
[InlineData(true)]
public void when_person_is_male_second_last_number_is_even(bool isSamordningsnummer)
{
var f = new Faker("sv");
var person = f.Person;
person.Gender = Name.Gender.Male;

var identificationNumber = isSamordningsnummer ? person.Samordningsnummer() : person.Personnummer();

var secondLast = int.Parse(identificationNumber.Substring(identificationNumber.Length - 2, 1));

secondLast.Should()
.Match(x => x % 2 == 0)
.And.BeLessThan(10)
.And.BeGreaterThan(0);
}

[Fact]
public void when_person_is_female_second_last_number_is_odd()
[Theory]
[InlineData(false)]
[InlineData(true)]
public void when_person_is_female_second_last_number_is_odd(bool isSamordningsnummer)
{
var f = new Faker("sv");
var person = f.Person;
person.Gender = Name.Gender.Female;

var personnummer = person.Personnummer();
var identificationNumber = isSamordningsnummer ? person.Samordningsnummer() : person.Personnummer();

var secondLast = int.Parse(personnummer.Substring(personnummer.Length - 2, 1));
var secondLast = int.Parse(identificationNumber.Substring(identificationNumber.Length - 2, 1));

secondLast.Should()
.Match(x => x % 2 == 1)
Expand All @@ -65,4 +106,9 @@ private static bool CheckLuhn(string digits)
)
.Sum() % 10 == 0;
}

private static (int year, int month, int day) ExtractDateParts(string date)
{
return (int.Parse(date.Substring(0, 4)), int.Parse(date.Substring(4, 2)), int.Parse(date.Substring(6, 2)));
}
}
39 changes: 37 additions & 2 deletions Source/Bogus/Extensions/Sweden/ExtensionsForSweden.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,46 @@ public static string Personnummer(this Person person)
return individualNumber;
}

/// <summary>
/// Swedish coordination number
/// </summary>
/// <remarks>
/// Coordination numbers enable Swedish public authorities and other organisations with a public function
/// to identify people who are not currently – and have never been – registered at an address in Sweden.
/// </remarks>
public static string Samordningsnummer(this Person person)
{
const string key = nameof(ExtensionsForSweden) + nameof(Samordningsnummer);
if( person.context.ContainsKey(key) )
{
return person.context[key] as string;
}

/*
YYYYMMXXBBGC
| | | | ||---> (C)Checksum
| | | | |
| | | | |----> (G)Gender
| | | |------> (B)Birthplace
| | |--------> (X)Day+60
| |----------> (M)Month
|--------------> (Y)Year
info: https://skatteverket.se/privat/folkbokforing/samordningsnummer.4.5c281c7015abecc2e201130b.html
*/
var r = person.Random;
var individualNumber = GenerateIndividualNumber(r, person.Gender, person.DateOfBirth, 60);

person.context[key] = individualNumber;
return individualNumber;
}

private static string GenerateIndividualNumber(Randomizer r, Gender gender, DateTime dateOfBirth)
private static string GenerateIndividualNumber(Randomizer r, Gender gender, DateTime dateOfBirth, int dayOffset = 0)
{
var genderNumber = GetGenderNumber(r, gender);
var p = dateOfBirth.ToString("yyyyMMddff") + genderNumber;
var offsetDay = dateOfBirth.Day + dayOffset;
var p = dateOfBirth.ToString("yyyyMM") + offsetDay.ToString("00") + dateOfBirth.ToString("ff") + genderNumber;
var checksum = GetLuhn(p.Substring(2));
return p + checksum;
}
Expand Down

0 comments on commit 78d25fc

Please sign in to comment.