Files
server/test/SeederApi.IntegrationTest/GeneratorContextTests.cs

263 lines
8.1 KiB
C#

using Bit.Seeder.Data;
using Bit.Seeder.Data.Distributions;
using Bit.Seeder.Data.Enums;
using Bit.Seeder.Options;
using Xunit;
namespace Bit.SeederApi.IntegrationTest;
public class GeneratorContextTests
{
[Fact]
public void FromOptions_SameDomain_ProducesSameSeed()
{
var options1 = CreateOptions("acme.com", ciphers: 100);
var options2 = CreateOptions("acme.com", ciphers: 100);
var ctx1 = GeneratorContext.FromOptions(options1);
var ctx2 = GeneratorContext.FromOptions(options2);
Assert.Equal(ctx1.Seed, ctx2.Seed);
}
[Fact]
public void FromOptions_DifferentDomains_ProduceDifferentSeeds()
{
var ctx1 = GeneratorContext.FromOptions(CreateOptions("acme.com"));
var ctx2 = GeneratorContext.FromOptions(CreateOptions("contoso.com"));
Assert.NotEqual(ctx1.Seed, ctx2.Seed);
}
[Fact]
public void FromOptions_ExplicitSeed_OverridesDomainHash()
{
var options = new OrganizationVaultOptions
{
Name = "Test Org",
Domain = "example.com",
Users = 10,
Ciphers = 100,
Seed = 42
};
var ctx = GeneratorContext.FromOptions(options);
Assert.Equal(42, ctx.Seed);
}
[Fact]
public void Username_SameSeed_ProducesSameOutput()
{
var options = CreateOptions("test.com", ciphers: 100);
var ctx1 = GeneratorContext.FromOptions(options);
var ctx2 = GeneratorContext.FromOptions(options);
for (int i = 0; i < 50; i++)
{
var username1 = ctx1.Username.GenerateByIndex(i, totalHint: 100, domain: "test.com");
var username2 = ctx2.Username.GenerateByIndex(i, totalHint: 100, domain: "test.com");
Assert.Equal(username1, username2);
}
}
[Fact]
public void Username_DifferentSeeds_ProducesDifferentOutput()
{
var ctx1 = GeneratorContext.FromOptions(CreateOptions("alpha.com"));
var ctx2 = GeneratorContext.FromOptions(CreateOptions("beta.com"));
var username1 = ctx1.Username.GenerateByIndex(0, domain: "alpha.com");
var username2 = ctx2.Username.GenerateByIndex(0, domain: "beta.com");
Assert.NotEqual(username1, username2);
}
[Fact]
public void Folder_SameSeed_ProducesSameOutput()
{
var options = CreateOptions("test.com");
var ctx1 = GeneratorContext.FromOptions(options);
var ctx2 = GeneratorContext.FromOptions(options);
for (int i = 0; i < 20; i++)
{
var folder1 = ctx1.Folder.GetFolderName(i);
var folder2 = ctx2.Folder.GetFolderName(i);
Assert.Equal(folder1, folder2);
}
}
[Fact]
public void Card_SameSeed_ProducesSameOutput()
{
var options = CreateOptions("test.com");
var ctx1 = GeneratorContext.FromOptions(options);
var ctx2 = GeneratorContext.FromOptions(options);
for (int i = 0; i < 20; i++)
{
var card1 = ctx1.Card.GenerateByIndex(i);
var card2 = ctx2.Card.GenerateByIndex(i);
Assert.Equal(card1.CardholderName, card2.CardholderName);
Assert.Equal(card1.Number, card2.Number);
Assert.Equal(card1.ExpMonth, card2.ExpMonth);
Assert.Equal(card1.ExpYear, card2.ExpYear);
Assert.Equal(card1.Code, card2.Code);
}
}
[Fact]
public void Identity_SameSeed_ProducesSameOutput()
{
var options = CreateOptions("test.com");
var ctx1 = GeneratorContext.FromOptions(options);
var ctx2 = GeneratorContext.FromOptions(options);
for (int i = 0; i < 20; i++)
{
var identity1 = ctx1.Identity.GenerateByIndex(i);
var identity2 = ctx2.Identity.GenerateByIndex(i);
Assert.Equal(identity1.FirstName, identity2.FirstName);
Assert.Equal(identity1.LastName, identity2.LastName);
Assert.Equal(identity1.Email, identity2.Email);
}
}
/// <summary>
/// Limited to 5 iterations to avoid a Bogus.Password() infinite loop bug
/// that occurs with certain seed/index combinations in WiFi/Database note categories.
/// The workaround is a known test workaround that doesn't affect production code.
/// </summary>
[Fact]
public void SecureNote_SameSeed_ProducesSameOutput()
{
var options = CreateOptions("test.com");
var ctx1 = GeneratorContext.FromOptions(options);
var ctx2 = GeneratorContext.FromOptions(options);
for (var i = 0; i < 5; i++)
{
var (title1, content1) = ctx1.SecureNote.GenerateByIndex(i);
var (title2, content2) = ctx2.SecureNote.GenerateByIndex(i);
Assert.Equal(title1, title2);
Assert.Equal(content1, content2);
}
}
[Fact]
public void CipherCount_ReflectsOptionsValue()
{
var options = CreateOptions("test.com", ciphers: 500);
var ctx = GeneratorContext.FromOptions(options);
Assert.Equal(500, ctx.CipherCount);
}
[Fact]
public void Username_WithCorporatePattern_AppliesCorrectFormat()
{
var options = new OrganizationVaultOptions
{
Name = "Test Org",
Domain = "corp.com",
Users = 10,
Ciphers = 100,
UsernamePattern = UsernamePatternType.FDotLast,
UsernameDistribution = new Distribution<UsernameCategory>(
(UsernameCategory.CorporateEmail, 1.0)
)
};
var ctx = GeneratorContext.FromOptions(options);
var username = ctx.Username.GenerateByIndex(0, domain: "corp.com");
Assert.Matches(@"^[a-z]\.[a-z]+@corp\.com$", username);
}
[Fact]
public void Username_WithRegion_ProducesCulturallyAppropriateNames()
{
var europeOptions = new OrganizationVaultOptions
{
Name = "Euro Corp",
Domain = "euro.com",
Users = 10,
Ciphers = 100,
Region = GeographicRegion.Europe,
UsernameDistribution = new Distribution<UsernameCategory>(
(UsernameCategory.CorporateEmail, 1.0)
)
};
var ctx = GeneratorContext.FromOptions(europeOptions);
var username = ctx.Username.GenerateByIndex(0, domain: "euro.com");
Assert.Contains("@euro.com", username);
Assert.Matches(@"^[\p{L}]+\.[\p{L}]+@euro\.com$", username);
}
[Fact]
public void Generators_AreLazilyInitialized()
{
var options = CreateOptions("test.com");
var ctx = GeneratorContext.FromOptions(options);
_ = ctx.Seed;
_ = ctx.Username.GenerateByIndex(0);
Assert.NotNull(ctx.Username);
Assert.NotNull(ctx.Folder);
Assert.NotNull(ctx.Card);
Assert.NotNull(ctx.Identity);
}
[Fact]
public void AllGenerators_ProduceDifferentOutputForDifferentIndices()
{
var ctx = GeneratorContext.FromOptions(CreateOptions("test.com", ciphers: 100));
var usernames = Enumerable.Range(0, 50)
.Select(i => ctx.Username.GenerateByIndex(i, domain: "test.com"))
.ToHashSet();
Assert.True(usernames.Count > 40, "Should generate mostly unique usernames");
var folders = Enumerable.Range(0, 50)
.Select(i => ctx.Folder.GetFolderName(i))
.ToHashSet();
Assert.True(folders.Count > 30, "Should generate diverse folder names");
var cards = Enumerable.Range(0, 50)
.Select(i => ctx.Card.GenerateByIndex(i).Number)
.ToHashSet();
Assert.True(cards.Count > 40, "Should generate mostly unique card numbers");
var identities = Enumerable.Range(0, 50)
.Select(i => ctx.Identity.GenerateByIndex(i).Email)
.ToHashSet();
Assert.True(identities.Count > 40, "Should generate mostly unique identity emails");
}
private static OrganizationVaultOptions CreateOptions(string domain, int ciphers = 100)
{
return new OrganizationVaultOptions
{
Name = "Test Org",
Domain = domain,
Users = 10,
Ciphers = ciphers
};
}
}