mirror of
https://github.com/bitwarden/server.git
synced 2026-02-10 02:43:17 +08:00
263 lines
8.1 KiB
C#
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
|
|
};
|
|
}
|
|
}
|