Move source code into "Source" folder
This commit is contained in:
parent
1ee73fa1a7
commit
389fbb2371
287 changed files with 26 additions and 8 deletions
593
Source/_Tests/AudibleUtilities.Tests/AccountTests.cs
Normal file
593
Source/_Tests/AudibleUtilities.Tests/AccountTests.cs
Normal file
|
|
@ -0,0 +1,593 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using AudibleApi;
|
||||
using AudibleApi.Authorization;
|
||||
using AudibleUtilities;
|
||||
using Dinah.Core;
|
||||
using FluentAssertions;
|
||||
using FluentAssertions.Common;
|
||||
using Microsoft.VisualStudio.TestPlatform.Common.Filtering;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
using Moq.Protected;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace AccountsTests
|
||||
{
|
||||
public class AccountsTestBase
|
||||
{
|
||||
protected const string EMPTY_FILE = "{\r\n \"Accounts\": []\r\n}";
|
||||
|
||||
protected string TestFile;
|
||||
protected Locale usLocale => Localization.Get("us");
|
||||
protected Locale ukLocale => Localization.Get("uk");
|
||||
|
||||
protected void WriteToTestFile(string contents)
|
||||
=> File.WriteAllText(TestFile, contents);
|
||||
|
||||
[TestInitialize]
|
||||
public void TestInit()
|
||||
=> TestFile = Guid.NewGuid() + ".txt";
|
||||
|
||||
[TestCleanup]
|
||||
public void TestCleanup()
|
||||
{
|
||||
if (File.Exists(TestFile))
|
||||
File.Delete(TestFile);
|
||||
}
|
||||
}
|
||||
|
||||
[TestClass]
|
||||
public class FromJson : AccountsTestBase
|
||||
{
|
||||
[TestMethod]
|
||||
public void _0_accounts()
|
||||
{
|
||||
var accountsSettings = AccountsSettings.FromJson(EMPTY_FILE);
|
||||
accountsSettings.Accounts.Count.Should().Be(0);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void _1_account_new()
|
||||
{
|
||||
var json = @"
|
||||
{
|
||||
""Accounts"": [
|
||||
{
|
||||
""AccountId"": ""cng"",
|
||||
""AccountName"": ""my main login"",
|
||||
""DecryptKey"": ""asdfasdf"",
|
||||
""IdentityTokens"": null
|
||||
}
|
||||
]
|
||||
}
|
||||
".Trim();
|
||||
var accountsSettings = AccountsSettings.FromJson(json);
|
||||
accountsSettings.Accounts.Count.Should().Be(1);
|
||||
accountsSettings.Accounts[0].AccountId.Should().Be("cng");
|
||||
accountsSettings.Accounts[0].IdentityTokens.Should().BeNull();
|
||||
}
|
||||
}
|
||||
|
||||
[TestClass]
|
||||
public class ctor : AccountsTestBase
|
||||
{
|
||||
[TestMethod]
|
||||
public void create_file()
|
||||
{
|
||||
File.Exists(TestFile).Should().BeFalse();
|
||||
var accountsSettings = new AccountsSettings();
|
||||
_ = new AccountsSettingsPersister(accountsSettings, TestFile);
|
||||
File.Exists(TestFile).Should().BeTrue();
|
||||
File.ReadAllText(TestFile).Should().Be(EMPTY_FILE);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void overwrite_existing_file()
|
||||
{
|
||||
File.Exists(TestFile).Should().BeFalse();
|
||||
WriteToTestFile("foo");
|
||||
File.Exists(TestFile).Should().BeTrue();
|
||||
|
||||
var accountsSettings = new AccountsSettings();
|
||||
_ = new AccountsSettingsPersister(accountsSettings, TestFile);
|
||||
File.Exists(TestFile).Should().BeTrue();
|
||||
File.ReadAllText(TestFile).Should().Be(EMPTY_FILE);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void save_multiple_children()
|
||||
{
|
||||
var accountsSettings = new AccountsSettings();
|
||||
accountsSettings.Add(new Account("a0") { AccountName = "n0" });
|
||||
accountsSettings.Add(new Account("a1") { AccountName = "n1" });
|
||||
|
||||
// dispose to cease auto-updates
|
||||
using (var p = new AccountsSettingsPersister(accountsSettings, TestFile)) { }
|
||||
|
||||
var persister = new AccountsSettingsPersister(TestFile);
|
||||
persister.AccountsSettings.Accounts.Count.Should().Be(2);
|
||||
persister.AccountsSettings.Accounts[1].AccountName.Should().Be("n1");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void save_with_identity()
|
||||
{
|
||||
var id = new Identity(usLocale);
|
||||
var idJson = JsonConvert.SerializeObject(id, Identity.GetJsonSerializerSettings());
|
||||
|
||||
var accountsSettings = new AccountsSettings();
|
||||
accountsSettings.Add(new Account("a0") { AccountName = "n0", IdentityTokens = id });
|
||||
|
||||
// dispose to cease auto-updates
|
||||
using (var p = new AccountsSettingsPersister(accountsSettings, TestFile)) { }
|
||||
|
||||
var persister = new AccountsSettingsPersister(TestFile);
|
||||
var acct = persister.AccountsSettings.Accounts[0];
|
||||
acct.AccountName.Should().Be("n0");
|
||||
acct.Locale.CountryCode.Should().Be("us");
|
||||
}
|
||||
}
|
||||
|
||||
[TestClass]
|
||||
public class save : AccountsTestBase
|
||||
{
|
||||
// add/save account after file creation
|
||||
[TestMethod]
|
||||
public void save_1_account()
|
||||
{
|
||||
// create initial file
|
||||
using (var p = new AccountsSettingsPersister(new AccountsSettings(), TestFile)) { }
|
||||
|
||||
// load file. create account
|
||||
using (var p = new AccountsSettingsPersister(TestFile))
|
||||
{
|
||||
var idIn = new Identity(usLocale);
|
||||
var acctIn = new Account("a0") { AccountName = "n0", IdentityTokens = idIn };
|
||||
|
||||
p.AccountsSettings.Add(acctIn);
|
||||
}
|
||||
|
||||
// re-load file. ensure account still exists
|
||||
using (var p = new AccountsSettingsPersister(TestFile))
|
||||
{
|
||||
p.AccountsSettings.Accounts.Count.Should().Be(1);
|
||||
var acct0 = p.AccountsSettings.Accounts[0];
|
||||
acct0.AccountName.Should().Be("n0");
|
||||
acct0.Locale.CountryCode.Should().Be("us");
|
||||
}
|
||||
}
|
||||
|
||||
// add/save mult accounts after file creation
|
||||
// separately create 2 accounts. ensure both still exist in the end
|
||||
[TestMethod]
|
||||
public void save_2_accounts()
|
||||
{
|
||||
// create initial file
|
||||
using (var p = new AccountsSettingsPersister(new AccountsSettings(), TestFile)) { }
|
||||
|
||||
// load file. create account 0
|
||||
using (var p = new AccountsSettingsPersister(TestFile))
|
||||
{
|
||||
var idIn = new Identity(usLocale);
|
||||
var acctIn = new Account("a0") { AccountName = "n0", IdentityTokens = idIn };
|
||||
|
||||
p.AccountsSettings.Add(acctIn);
|
||||
}
|
||||
|
||||
// re-load file. ensure account still exists
|
||||
using (var p = new AccountsSettingsPersister(TestFile))
|
||||
{
|
||||
p.AccountsSettings.Accounts.Count.Should().Be(1);
|
||||
|
||||
var acct0 = p.AccountsSettings.Accounts[0];
|
||||
acct0.AccountName.Should().Be("n0");
|
||||
acct0.Locale.CountryCode.Should().Be("us");
|
||||
}
|
||||
|
||||
// load file. create account 1
|
||||
using (var p = new AccountsSettingsPersister(TestFile))
|
||||
{
|
||||
var idIn = new Identity(ukLocale);
|
||||
var acctIn = new Account("a1") { AccountName = "n1", IdentityTokens = idIn };
|
||||
|
||||
p.AccountsSettings.Add(acctIn);
|
||||
}
|
||||
|
||||
// re-load file. ensure both accounts still exist
|
||||
using (var p = new AccountsSettingsPersister(TestFile))
|
||||
{
|
||||
p.AccountsSettings.Accounts.Count.Should().Be(2);
|
||||
|
||||
var acct0 = p.AccountsSettings.Accounts[0];
|
||||
acct0.AccountName.Should().Be("n0");
|
||||
acct0.Locale.CountryCode.Should().Be("us");
|
||||
|
||||
var acct1 = p.AccountsSettings.Accounts[1];
|
||||
acct1.AccountName.Should().Be("n1");
|
||||
acct1.Locale.CountryCode.Should().Be("uk");
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void update_Account_field_just_added()
|
||||
{
|
||||
// create initial file
|
||||
using (var p = new AccountsSettingsPersister(new AccountsSettings(), TestFile)) { }
|
||||
|
||||
// load file. create 2 accounts
|
||||
using (var p = new AccountsSettingsPersister(TestFile))
|
||||
{
|
||||
var id1 = new Identity(usLocale);
|
||||
var acct1 = new Account("a0") { AccountName = "n0", IdentityTokens = id1 };
|
||||
p.AccountsSettings.Add(acct1);
|
||||
|
||||
// update just-added item. note: this is different than the subscription which happens on initial collection load. ensure this works also
|
||||
acct1.AccountName = "new";
|
||||
}
|
||||
|
||||
// verify save property
|
||||
using (var p = new AccountsSettingsPersister(TestFile))
|
||||
{
|
||||
var acct0 = p.AccountsSettings.Accounts[0];
|
||||
acct0.AccountName.Should().Be("new");
|
||||
}
|
||||
}
|
||||
|
||||
// update Account property. must be non-destructive to all other data
|
||||
[TestMethod]
|
||||
public void update_Account_field()
|
||||
{
|
||||
// create initial file
|
||||
using (var p = new AccountsSettingsPersister(new AccountsSettings(), TestFile)) { }
|
||||
|
||||
// load file. create 2 accounts
|
||||
using (var p = new AccountsSettingsPersister(TestFile))
|
||||
{
|
||||
var id1 = new Identity(usLocale);
|
||||
var acct1 = new Account("a0") { AccountName = "n0", IdentityTokens = id1 };
|
||||
p.AccountsSettings.Add(acct1);
|
||||
|
||||
var id2 = new Identity(ukLocale);
|
||||
var acct2 = new Account("a1") { AccountName = "n1", IdentityTokens = id2 };
|
||||
|
||||
p.AccountsSettings.Add(acct2);
|
||||
}
|
||||
|
||||
// update AccountName on existing file
|
||||
using (var p = new AccountsSettingsPersister(TestFile))
|
||||
{
|
||||
var acct0 = p.AccountsSettings.Accounts[0];
|
||||
acct0.AccountName = "new";
|
||||
}
|
||||
|
||||
// re-load file. ensure both accounts still exist
|
||||
using (var p = new AccountsSettingsPersister(TestFile))
|
||||
{
|
||||
p.AccountsSettings.Accounts.Count.Should().Be(2);
|
||||
|
||||
var acct0 = p.AccountsSettings.Accounts[0];
|
||||
// new
|
||||
acct0.AccountName.Should().Be("new");
|
||||
|
||||
// still here
|
||||
acct0.Locale.CountryCode.Should().Be("us");
|
||||
var acct1 = p.AccountsSettings.Accounts[1];
|
||||
acct1.AccountName.Should().Be("n1");
|
||||
acct1.Locale.CountryCode.Should().Be("uk");
|
||||
}
|
||||
}
|
||||
|
||||
// update identity. must be non-destructive to all other data
|
||||
[TestMethod]
|
||||
public void replace_identity()
|
||||
{
|
||||
// create initial file
|
||||
using (var p = new AccountsSettingsPersister(new AccountsSettings(), TestFile)) { }
|
||||
|
||||
// load file. create 2 accounts
|
||||
using (var p = new AccountsSettingsPersister(TestFile))
|
||||
{
|
||||
var id1 = new Identity(usLocale);
|
||||
var acct1 = new Account("a0") { AccountName = "n0", IdentityTokens = id1 };
|
||||
p.AccountsSettings.Add(acct1);
|
||||
|
||||
var id2 = new Identity(ukLocale);
|
||||
var acct2 = new Account("a1") { AccountName = "n1", IdentityTokens = id2 };
|
||||
|
||||
p.AccountsSettings.Add(acct2);
|
||||
}
|
||||
|
||||
// update identity on existing file
|
||||
using (var p = new AccountsSettingsPersister(TestFile))
|
||||
{
|
||||
var id = new Identity(ukLocale);
|
||||
|
||||
var acct0 = p.AccountsSettings.Accounts[0];
|
||||
acct0.IdentityTokens = id;
|
||||
}
|
||||
|
||||
// re-load file. ensure both accounts still exist
|
||||
using (var p = new AccountsSettingsPersister(TestFile))
|
||||
{
|
||||
p.AccountsSettings.Accounts.Count.Should().Be(2);
|
||||
|
||||
var acct0 = p.AccountsSettings.Accounts[0];
|
||||
// new
|
||||
acct0.Locale.CountryCode.Should().Be("uk");
|
||||
|
||||
// still here
|
||||
acct0.AccountName.Should().Be("n0");
|
||||
var acct1 = p.AccountsSettings.Accounts[1];
|
||||
acct1.AccountName.Should().Be("n1");
|
||||
acct1.Locale.CountryCode.Should().Be("uk");
|
||||
}
|
||||
}
|
||||
|
||||
// multi-level subscribe => update
|
||||
// edit field of existing identity. must be non-destructive to all other data
|
||||
[TestMethod]
|
||||
public void update_identity_field()
|
||||
{
|
||||
// create initial file
|
||||
using (var p = new AccountsSettingsPersister(new AccountsSettings(), TestFile)) { }
|
||||
|
||||
// load file. create 2 accounts
|
||||
using (var p = new AccountsSettingsPersister(TestFile))
|
||||
{
|
||||
var id1 = new Identity(usLocale);
|
||||
var acct1 = new Account("a0") { AccountName = "n0", IdentityTokens = id1 };
|
||||
p.AccountsSettings.Add(acct1);
|
||||
|
||||
var id2 = new Identity(ukLocale);
|
||||
var acct2 = new Account("a1") { AccountName = "n1", IdentityTokens = id2 };
|
||||
|
||||
p.AccountsSettings.Add(acct2);
|
||||
}
|
||||
|
||||
// update identity on existing file
|
||||
using (var p = new AccountsSettingsPersister(TestFile))
|
||||
{
|
||||
p.AccountsSettings.Accounts[0]
|
||||
.IdentityTokens
|
||||
.Update(new AccessToken("Atna|_NEW_", DateTime.Now.AddDays(1)));
|
||||
}
|
||||
|
||||
// re-load file. ensure both accounts still exist
|
||||
using (var p = new AccountsSettingsPersister(TestFile))
|
||||
{
|
||||
p.AccountsSettings.Accounts.Count.Should().Be(2);
|
||||
|
||||
var acct0 = p.AccountsSettings.Accounts[0];
|
||||
// new
|
||||
acct0.IdentityTokens.ExistingAccessToken.TokenValue.Should().Be("Atna|_NEW_");
|
||||
|
||||
// still here
|
||||
acct0.AccountName.Should().Be("n0");
|
||||
acct0.Locale.CountryCode.Should().Be("us");
|
||||
var acct1 = p.AccountsSettings.Accounts[1];
|
||||
acct1.AccountName.Should().Be("n1");
|
||||
acct1.Locale.CountryCode.Should().Be("uk");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[TestClass]
|
||||
public class retrieve : AccountsTestBase
|
||||
{
|
||||
[TestMethod]
|
||||
public void get_where()
|
||||
{
|
||||
var idUs = new Identity(usLocale);
|
||||
var acct1 = new Account("cng") { IdentityTokens = idUs, AccountName = "foo" };
|
||||
|
||||
var idUk = new Identity(ukLocale);
|
||||
var acct2 = new Account("cng") { IdentityTokens = idUk, AccountName = "bar" };
|
||||
|
||||
var accountsSettings = new AccountsSettings();
|
||||
accountsSettings.Add(acct1);
|
||||
accountsSettings.Add(acct2);
|
||||
|
||||
accountsSettings.GetAccount("cng", "uk").AccountName.Should().Be("bar");
|
||||
}
|
||||
}
|
||||
|
||||
[TestClass]
|
||||
public class upsert : AccountsTestBase
|
||||
{
|
||||
[TestMethod]
|
||||
public void upsert_new()
|
||||
{
|
||||
var accountsSettings = new AccountsSettings();
|
||||
accountsSettings.Accounts.Count.Should().Be(0);
|
||||
|
||||
accountsSettings.Upsert("cng", "us");
|
||||
|
||||
accountsSettings.Accounts.Count.Should().Be(1);
|
||||
accountsSettings.GetAccount("cng", "us").AccountId.Should().Be("cng");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void upsert_exists()
|
||||
{
|
||||
var accountsSettings = new AccountsSettings();
|
||||
var orig = accountsSettings.Upsert("cng", "us");
|
||||
orig.AccountName = "foo";
|
||||
|
||||
var exists = accountsSettings.Upsert("cng", "us");
|
||||
exists.AccountName.Should().Be("foo");
|
||||
|
||||
orig.Should().BeSameAs(exists);
|
||||
}
|
||||
}
|
||||
|
||||
[TestClass]
|
||||
public class delete : AccountsTestBase
|
||||
{
|
||||
[TestMethod]
|
||||
public void delete_account()
|
||||
{
|
||||
var accountsSettings = new AccountsSettings();
|
||||
var acct = accountsSettings.Upsert("cng", "us");
|
||||
accountsSettings.Accounts.Count.Should().Be(1);
|
||||
|
||||
var removed = accountsSettings.Delete(acct);
|
||||
removed.Should().BeTrue();
|
||||
|
||||
accountsSettings.Accounts.Count.Should().Be(0);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void delete_where()
|
||||
{
|
||||
var accountsSettings = new AccountsSettings();
|
||||
_ = accountsSettings.Upsert("cng", "us");
|
||||
accountsSettings.Accounts.Count.Should().Be(1);
|
||||
|
||||
accountsSettings.Delete("baz", "baz").Should().BeFalse();
|
||||
accountsSettings.Accounts.Count.Should().Be(1);
|
||||
|
||||
accountsSettings.Delete("cng", "us").Should().BeTrue();
|
||||
accountsSettings.Accounts.Count.Should().Be(0);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void delete_updates()
|
||||
{
|
||||
var i = 0;
|
||||
void update(object sender, EventArgs e) => i++;
|
||||
|
||||
var accountsSettings = new AccountsSettings();
|
||||
accountsSettings.Updated += update;
|
||||
|
||||
accountsSettings.Accounts.Count.Should().Be(0);
|
||||
i.Should().Be(0);
|
||||
|
||||
_ = accountsSettings.Upsert("cng", "us");
|
||||
accountsSettings.Accounts.Count.Should().Be(1);
|
||||
i.Should().Be(1);
|
||||
|
||||
accountsSettings.Delete("baz", "baz").Should().BeFalse();
|
||||
accountsSettings.Accounts.Count.Should().Be(1);
|
||||
i.Should().Be(1);
|
||||
|
||||
accountsSettings.Delete("cng", "us").Should().BeTrue();
|
||||
accountsSettings.Accounts.Count.Should().Be(0);
|
||||
i.Should().Be(2); // <== this is the one being tested
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void deleted_account_should_not_persist_file()
|
||||
{
|
||||
Account acct;
|
||||
|
||||
using (var p = new AccountsSettingsPersister(new AccountsSettings(), TestFile))
|
||||
{
|
||||
acct = p.AccountsSettings.Upsert("foo", "us");
|
||||
p.AccountsSettings.Accounts.Count.Should().Be(1);
|
||||
acct.AccountName = "old";
|
||||
}
|
||||
|
||||
using (var p = new AccountsSettingsPersister(new AccountsSettings(), TestFile))
|
||||
{
|
||||
p.AccountsSettings.Delete(acct);
|
||||
p.AccountsSettings.Accounts.Count.Should().Be(0);
|
||||
}
|
||||
|
||||
using (var p = new AccountsSettingsPersister(new AccountsSettings(), TestFile))
|
||||
{
|
||||
File.ReadAllText(TestFile).Should().Be(EMPTY_FILE);
|
||||
|
||||
acct.AccountName = "new";
|
||||
|
||||
File.ReadAllText(TestFile).Should().Be(EMPTY_FILE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// account.Id + Locale.Name -- must be unique
|
||||
[TestClass]
|
||||
public class validate : AccountsTestBase
|
||||
{
|
||||
[TestMethod]
|
||||
public void violate_validation()
|
||||
{
|
||||
var accountsSettings = new AccountsSettings();
|
||||
|
||||
var idIn = new Identity(usLocale);
|
||||
|
||||
var a1 = new Account("a") { AccountName = "one", IdentityTokens = idIn };
|
||||
accountsSettings.Add(a1);
|
||||
|
||||
var a2 = new Account("a") { AccountName = "two", IdentityTokens = idIn };
|
||||
|
||||
// violation: validate()
|
||||
Assert.ThrowsException<InvalidOperationException>(() => accountsSettings.Add(a2));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void identity_violate_validation()
|
||||
{
|
||||
var accountsSettings = new AccountsSettings();
|
||||
|
||||
var idIn = new Identity(usLocale);
|
||||
|
||||
var a1 = new Account("a") { AccountName = "one", IdentityTokens = idIn };
|
||||
accountsSettings.Add(a1);
|
||||
|
||||
var a2 = new Account("a") { AccountName = "two" };
|
||||
accountsSettings.Add(a2);
|
||||
|
||||
// violation: GetAccount.SingleOrDefault
|
||||
Assert.ThrowsException<InvalidOperationException>(() => a2.IdentityTokens = idIn);
|
||||
}
|
||||
}
|
||||
|
||||
[TestClass]
|
||||
public class transactions : AccountsTestBase
|
||||
{
|
||||
[TestMethod]
|
||||
public void atomic_update_at_end()
|
||||
{
|
||||
var p = new AccountsSettingsPersister(new AccountsSettings(), TestFile);
|
||||
p.BeginTransation();
|
||||
|
||||
// upserted account will not persist until CommitTransation
|
||||
var acct = p.AccountsSettings.Upsert("cng", "us");
|
||||
acct.AccountName = "foo";
|
||||
|
||||
File.ReadAllText(TestFile).Should().Be(EMPTY_FILE);
|
||||
p.IsInTransaction.Should().BeTrue();
|
||||
|
||||
p.CommitTransation();
|
||||
p.IsInTransaction.Should().BeFalse();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void abandoned_transaction()
|
||||
{
|
||||
var p = new AccountsSettingsPersister(new AccountsSettings(), TestFile);
|
||||
try
|
||||
{
|
||||
p.BeginTransation();
|
||||
|
||||
var acct = p.AccountsSettings.Upsert("cng", "us");
|
||||
acct.AccountName = "foo";
|
||||
throw new Exception();
|
||||
}
|
||||
catch { }
|
||||
finally
|
||||
{
|
||||
File.ReadAllText(TestFile).Should().Be(EMPTY_FILE);
|
||||
p.IsInTransaction.Should().BeTrue();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0-windows</TargetFramework>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FluentAssertions" Version="6.6.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
|
||||
<PackageReference Include="Moq" Version="4.17.2" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="2.2.10" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="2.2.10" />
|
||||
<PackageReference Include="coverlet.collector" Version="3.1.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\AudibleUtilities\AudibleUtilities.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
24
Source/_Tests/FileLiberator.Tests/FileLiberator.Tests.csproj
Normal file
24
Source/_Tests/FileLiberator.Tests/FileLiberator.Tests.csproj
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0-windows</TargetFramework>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FluentAssertions" Version="6.6.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="2.2.10" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="2.2.10" />
|
||||
<PackageReference Include="coverlet.collector" Version="3.1.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\FileLiberator\FileLiberator.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
24
Source/_Tests/FileManager.Tests/FileManager.Tests.csproj
Normal file
24
Source/_Tests/FileManager.Tests/FileManager.Tests.csproj
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FluentAssertions" Version="6.6.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="2.2.10" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="2.2.10" />
|
||||
<PackageReference Include="coverlet.collector" Version="3.1.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\FileManager\FileManager.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
115
Source/_Tests/FileManager.Tests/FileNamingTemplateTests.cs
Normal file
115
Source/_Tests/FileManager.Tests/FileNamingTemplateTests.cs
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Dinah.Core;
|
||||
using FileManager;
|
||||
using FluentAssertions;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace FileNamingTemplateTests
|
||||
{
|
||||
[TestClass]
|
||||
public class GetFilePath
|
||||
{
|
||||
[TestMethod]
|
||||
public void equiv_GetValidFilename()
|
||||
{
|
||||
var expected = @"C:\foo\bar\my_ book LONG_1234567890_1234567890_1234567890_123 [ID123456].txt";
|
||||
var f1 = OLD_GetValidFilename(@"C:\foo\bar", "my: book LONG_1234567890_1234567890_1234567890_12345", "txt", "ID123456");
|
||||
var f2 = NEW_GetValidFilename_FileNamingTemplate(@"C:\foo\bar", "my: book LONG_1234567890_1234567890_1234567890_12345", "txt", "ID123456");
|
||||
|
||||
f1.Should().Be(expected);
|
||||
f1.Should().Be(f2);
|
||||
}
|
||||
private static string OLD_GetValidFilename(string dirFullPath, string filename, string extension, string metadataSuffix)
|
||||
{
|
||||
ArgumentValidator.EnsureNotNullOrWhiteSpace(dirFullPath, nameof(dirFullPath));
|
||||
|
||||
filename ??= "";
|
||||
|
||||
// sanitize. omit invalid characters. exception: colon => underscore
|
||||
filename = filename.Replace(":", "_");
|
||||
filename = FileUtility.GetSafeFileName(filename);
|
||||
|
||||
if (filename.Length > 50)
|
||||
filename = filename.Substring(0, 50);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(metadataSuffix))
|
||||
filename += $" [{metadataSuffix}]";
|
||||
|
||||
// extension is null when this method is used for directory names
|
||||
extension = FileUtility.GetStandardizedExtension(extension);
|
||||
|
||||
// ensure uniqueness
|
||||
var fullfilename = Path.Combine(dirFullPath, filename + extension);
|
||||
var i = 0;
|
||||
while (File.Exists(fullfilename))
|
||||
fullfilename = Path.Combine(dirFullPath, filename + $" ({++i})" + extension);
|
||||
|
||||
return fullfilename;
|
||||
}
|
||||
private static string NEW_GetValidFilename_FileNamingTemplate(string dirFullPath, string filename, string extension, string metadataSuffix)
|
||||
{
|
||||
var template = $"<title> [<id>]";
|
||||
|
||||
var fullfilename = Path.Combine(dirFullPath, template + FileUtility.GetStandardizedExtension(extension));
|
||||
|
||||
var fileNamingTemplate = new FileNamingTemplate(fullfilename) { IllegalCharacterReplacements = "_" };
|
||||
fileNamingTemplate.AddParameterReplacement("title", filename);
|
||||
fileNamingTemplate.AddParameterReplacement("id", metadataSuffix);
|
||||
return fileNamingTemplate.GetFilePath();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void equiv_GetMultipartFileName()
|
||||
{
|
||||
var expected = @"C:\foo\bar\my file - 002 - title.txt";
|
||||
var f1 = OLD_GetMultipartFileName(@"C:\foo\bar\my file.txt", 2, 100, "title");
|
||||
var f2 = NEW_GetMultipartFileName_FileNamingTemplate(@"C:\foo\bar\my file.txt", 2, 100, "title");
|
||||
|
||||
f1.Should().Be(expected);
|
||||
f1.Should().Be(f2);
|
||||
}
|
||||
private static string OLD_GetMultipartFileName(string originalPath, int partsPosition, int partsTotal, string suffix)
|
||||
{
|
||||
// 1-9 => 1-9
|
||||
// 10-99 => 01-99
|
||||
// 100-999 => 001-999
|
||||
var chapterCountLeadingZeros = partsPosition.ToString().PadLeft(partsTotal.ToString().Length, '0');
|
||||
|
||||
string extension = Path.GetExtension(originalPath);
|
||||
|
||||
var filenameBase = $"{Path.GetFileNameWithoutExtension(originalPath)} - {chapterCountLeadingZeros}";
|
||||
if (!string.IsNullOrWhiteSpace(suffix))
|
||||
filenameBase += $" - {suffix}";
|
||||
|
||||
// Replace illegal path characters with spaces
|
||||
var fileName = FileUtility.GetSafeFileName(filenameBase, " ");
|
||||
var path = Path.Combine(Path.GetDirectoryName(originalPath), fileName + extension);
|
||||
return path;
|
||||
}
|
||||
private static string NEW_GetMultipartFileName_FileNamingTemplate(string originalPath, int partsPosition, int partsTotal, string suffix)
|
||||
{
|
||||
// 1-9 => 1-9
|
||||
// 10-99 => 01-99
|
||||
// 100-999 => 001-999
|
||||
var chapterCountLeadingZeros = partsPosition.ToString().PadLeft(partsTotal.ToString().Length, '0');
|
||||
|
||||
var t = Path.ChangeExtension(originalPath, null) + " - <chapter> - <title>" + Path.GetExtension(originalPath);
|
||||
|
||||
var fileNamingTemplate = new FileNamingTemplate(t) { IllegalCharacterReplacements = " " };
|
||||
fileNamingTemplate.AddParameterReplacement("chapter", chapterCountLeadingZeros);
|
||||
fileNamingTemplate.AddParameterReplacement("title", suffix);
|
||||
return fileNamingTemplate.GetFilePath();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void remove_slashes()
|
||||
{
|
||||
var fileNamingTemplate = new FileNamingTemplate(@"\foo\<title>.txt");
|
||||
fileNamingTemplate.AddParameterReplacement("title", @"s\l/a\s/h\e/s");
|
||||
fileNamingTemplate.GetFilePath().Should().Be(@"\foo\slashes.txt");
|
||||
}
|
||||
}
|
||||
}
|
||||
163
Source/_Tests/FileManager.Tests/FileUtilityTests.cs
Normal file
163
Source/_Tests/FileManager.Tests/FileUtilityTests.cs
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Dinah.Core;
|
||||
using FileManager;
|
||||
using FluentAssertions;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace FileUtilityTests
|
||||
{
|
||||
[TestClass]
|
||||
public class GetSafePath
|
||||
{
|
||||
[TestMethod]
|
||||
public void null_path_throws() => Assert.ThrowsException<ArgumentNullException>(() => FileUtility.GetSafePath(null));
|
||||
|
||||
// needs separate method. middle null param not running correctly in TestExplorer when used in DataRow()
|
||||
[TestMethod]
|
||||
[DataRow("http://test.com/a/b/c", @"http\test.com\a\b\c")]
|
||||
public void null_replacement(string inStr, string outStr) => Tests(inStr, null, outStr);
|
||||
|
||||
[TestMethod]
|
||||
// empty replacement
|
||||
[DataRow("abc*abc.txt", "", "abcabc.txt")]
|
||||
// non-empty replacement
|
||||
[DataRow("abc*abc.txt", "ZZZ", "abcZZZabc.txt")]
|
||||
// standardize slashes
|
||||
[DataRow(@"a/b\c/d", "Z", @"a\b\c\d")]
|
||||
// remove illegal chars
|
||||
[DataRow("a*?:z.txt", "Z", "aZZZz.txt")]
|
||||
// retain drive letter path colon
|
||||
[DataRow(@"C:\az.txt", "Z", @"C:\az.txt")]
|
||||
// replace all other colons
|
||||
[DataRow(@"a\b:c\d.txt", "ZZZ", @"a\bZZZc\d.txt")]
|
||||
// remove empty directories
|
||||
[DataRow(@"C:\a\\\b\c\\\d.txt", "ZZZ", @"C:\a\b\c\d.txt")]
|
||||
[DataRow(@"C:\""foo\<id>", "ZZZ", @"C:\ZZZfoo\ZZZidZZZ")]
|
||||
public void Tests(string inStr, string replacement, string outStr) => Assert.AreEqual(outStr, FileUtility.GetSafePath(inStr, replacement));
|
||||
}
|
||||
|
||||
[TestClass]
|
||||
public class GetSafeFileName
|
||||
{
|
||||
// needs separate method. middle null param not running correctly in TestExplorer when used in DataRow()
|
||||
[TestMethod]
|
||||
[DataRow("http://test.com/a/b/c", "httptest.comabc")]
|
||||
public void url_null_replacement(string inStr, string outStr) => ReplacementTests(inStr, null, outStr);
|
||||
|
||||
[TestMethod]
|
||||
// empty replacement
|
||||
[DataRow("http://test.com/a/b/c", "", "httptest.comabc")]
|
||||
// single char replace
|
||||
[DataRow("http://test.com/a/b/c", "_", "http___test.com_a_b_c")]
|
||||
// multi char replace
|
||||
[DataRow("http://test.com/a/b/c", "!!!", "http!!!!!!!!!test.com!!!a!!!b!!!c")]
|
||||
public void ReplacementTests(string inStr, string replacement, string outStr) => FileUtility.GetSafeFileName(inStr, replacement).Should().Be(outStr);
|
||||
}
|
||||
|
||||
[TestClass]
|
||||
public class GetSequenceFormatted
|
||||
{
|
||||
[TestMethod]
|
||||
public void negative_partsPosition() => Assert.ThrowsException<ArgumentException>(()
|
||||
=> FileUtility.GetSequenceFormatted(-1, 2)
|
||||
);
|
||||
[TestMethod]
|
||||
public void zero_partsPosition() => Assert.ThrowsException<ArgumentException>(()
|
||||
=> FileUtility.GetSequenceFormatted(0, 2)
|
||||
);
|
||||
|
||||
[TestMethod]
|
||||
public void negative_partsTotal() => Assert.ThrowsException<ArgumentException>(()
|
||||
=> FileUtility.GetSequenceFormatted(2, -1)
|
||||
);
|
||||
[TestMethod]
|
||||
public void zero_partsTotal() => Assert.ThrowsException<ArgumentException>(()
|
||||
=> FileUtility.GetSequenceFormatted(2, 0)
|
||||
);
|
||||
|
||||
[TestMethod]
|
||||
public void partsPosition_greater_than_partsTotal() => Assert.ThrowsException<ArgumentException>(()
|
||||
=> FileUtility.GetSequenceFormatted(2, 1)
|
||||
);
|
||||
|
||||
[TestMethod]
|
||||
// only part
|
||||
[DataRow(1, 1, "1")]
|
||||
// 2 digits
|
||||
[DataRow(2, 90, "02")]
|
||||
// 3 digits
|
||||
[DataRow(2, 900, "002")]
|
||||
public void Tests(int partsPosition, int partsTotal, string expected)
|
||||
=> FileUtility.GetSequenceFormatted(partsPosition, partsTotal).Should().Be(expected);
|
||||
}
|
||||
|
||||
[TestClass]
|
||||
public class GetStandardizedExtension
|
||||
{
|
||||
[TestMethod]
|
||||
public void is_null() => Tests(null, "");
|
||||
|
||||
[TestMethod]
|
||||
public void is_empty() => Tests("", "");
|
||||
|
||||
[TestMethod]
|
||||
public void is_whitespace() => Tests(" ", "");
|
||||
|
||||
[TestMethod]
|
||||
[DataRow("txt", ".txt")]
|
||||
[DataRow(".txt", ".txt")]
|
||||
[DataRow(" .txt ", ".txt")]
|
||||
public void Tests(string input, string expected)
|
||||
=> FileUtility.GetStandardizedExtension(input).Should().Be(expected);
|
||||
}
|
||||
|
||||
[TestClass]
|
||||
public class GetValidFilename
|
||||
{
|
||||
[TestMethod]
|
||||
// dot-files
|
||||
[DataRow(@"C:\a bc\x y z\.f i l e.txt")]
|
||||
// dot-folders
|
||||
[DataRow(@"C:\a bc\.x y z\f i l e.txt")]
|
||||
public void Valid(string input) => Tests(input, input);
|
||||
|
||||
[TestMethod]
|
||||
// folder spaces
|
||||
[DataRow(@"C:\ a bc \x y z ", @"C:\a bc\x y z")]
|
||||
// file spaces
|
||||
[DataRow(@"C:\a bc\x y z\ f i l e.txt ", @"C:\a bc\x y z\f i l e.txt")]
|
||||
// eliminate beginning space and end dots and spaces
|
||||
[DataRow(@"C:\a bc\ . . . x y z . . . \f i l e.txt", @"C:\a bc\. . . x y z\f i l e.txt")]
|
||||
// file end dots
|
||||
[DataRow(@"C:\a bc\x y z\f i l e.txt . . .", @"C:\a bc\x y z\f i l e.txt")]
|
||||
public void Tests(string input, string expected)
|
||||
=> FileUtility.GetValidFilename(input).Should().Be(expected);
|
||||
}
|
||||
|
||||
[TestClass]
|
||||
public class RemoveLastCharacter
|
||||
{
|
||||
[TestMethod]
|
||||
public void is_null() => Tests(null, null);
|
||||
|
||||
[TestMethod]
|
||||
public void empty() => Tests("", "");
|
||||
|
||||
[TestMethod]
|
||||
public void single_space() => Tests(" ", "");
|
||||
|
||||
[TestMethod]
|
||||
public void multiple_space() => Tests(" ", " ");
|
||||
|
||||
[TestMethod]
|
||||
[DataRow("1", "")]
|
||||
[DataRow("1 ", "1")]
|
||||
[DataRow("12", "1")]
|
||||
[DataRow("123", "12")]
|
||||
public void Tests(string input, string expected)
|
||||
=> FileUtility.RemoveLastCharacter(input).Should().Be(expected);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0-windows</TargetFramework>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FluentAssertions" Version="6.6.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="2.2.10" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="2.2.10" />
|
||||
<PackageReference Include="coverlet.collector" Version="3.1.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\LibationFileManager\LibationFileManager.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
393
Source/_Tests/LibationFileManager.Tests/TemplatesTests.cs
Normal file
393
Source/_Tests/LibationFileManager.Tests/TemplatesTests.cs
Normal file
|
|
@ -0,0 +1,393 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Dinah.Core;
|
||||
using FluentAssertions;
|
||||
using LibationFileManager;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
using static TemplatesTests.Shared;
|
||||
|
||||
namespace TemplatesTests
|
||||
{
|
||||
public static class Shared
|
||||
{
|
||||
public static LibraryBookDto GetLibraryBook(string asin, string seriesName = "Sherlock Holmes")
|
||||
=> new()
|
||||
{
|
||||
Account = "my account",
|
||||
AudibleProductId = asin,
|
||||
Title = "A Study in Scarlet: A Sherlock Holmes Novel",
|
||||
Locale = "us",
|
||||
Authors = new List<string> { "Arthur Conan Doyle", "Stephen Fry - introductions" },
|
||||
Narrators = new List<string> { "Stephen Fry" },
|
||||
SeriesName = seriesName ?? "",
|
||||
SeriesNumber = "1"
|
||||
};
|
||||
}
|
||||
|
||||
[TestClass]
|
||||
public class ContainsChapterOnlyTags
|
||||
{
|
||||
[TestMethod]
|
||||
[DataRow("<ch>", false)]
|
||||
[DataRow("<ch#>", true)]
|
||||
[DataRow("<id>", false)]
|
||||
[DataRow("<id><ch#>", true)]
|
||||
public void Tests(string template, bool expected) => Templates.ContainsChapterOnlyTags(template).Should().Be(expected);
|
||||
}
|
||||
|
||||
[TestClass]
|
||||
public class ContainsTag
|
||||
{
|
||||
[TestMethod]
|
||||
[DataRow("<ch#>", "ch#", true)]
|
||||
[DataRow("<id>", "ch#", false)]
|
||||
[DataRow("<id><ch#>", "ch#", true)]
|
||||
public void Tests(string template, string tag, bool expected) => Templates.ContainsTag(template, tag).Should().Be(expected);
|
||||
}
|
||||
|
||||
[TestClass]
|
||||
public class getFileNamingTemplate
|
||||
{
|
||||
[TestMethod]
|
||||
[DataRow(null, "asin", @"C:\", "ext")]
|
||||
[ExpectedException(typeof(ArgumentNullException))]
|
||||
public void arg_null_exception(string template, string asin, string dirFullPath, string extension)
|
||||
=> Templates.getFileNamingTemplate(GetLibraryBook(asin), template, dirFullPath, extension);
|
||||
|
||||
[TestMethod]
|
||||
[DataRow("", "asin", @"C:\foo\bar", "ext")]
|
||||
[DataRow(" ", "asin", @"C:\foo\bar", "ext")]
|
||||
[ExpectedException(typeof(ArgumentException))]
|
||||
public void arg_exception(string template, string asin, string dirFullPath, string extension)
|
||||
=> Templates.getFileNamingTemplate(GetLibraryBook(asin), template, dirFullPath, extension);
|
||||
|
||||
[TestMethod]
|
||||
public void null_extension() => Tests("f.txt", "asin", @"C:\foo\bar", null, @"C:\foo\bar\f.txt");
|
||||
|
||||
[TestMethod]
|
||||
[DataRow("f.txt", "asin", @"C:\foo\bar", "ext", @"C:\foo\bar\f.txt.ext")]
|
||||
[DataRow("f", "asin", @"C:\foo\bar", "ext", @"C:\foo\bar\f.ext")]
|
||||
[DataRow("<id>", "asin", @"C:\foo\bar", "ext", @"C:\foo\bar\asin.ext")]
|
||||
public void Tests(string template, string asin, string dirFullPath, string extension, string expected)
|
||||
=> Templates.getFileNamingTemplate(GetLibraryBook(asin), template, dirFullPath, extension)
|
||||
.GetFilePath()
|
||||
.Should().Be(expected);
|
||||
|
||||
[TestMethod]
|
||||
public void IfSeries_empty()
|
||||
=> Templates.getFileNamingTemplate(GetLibraryBook("asin", "Sherlock Holmes"), "foo<if series-><-if series>bar", @"C:\a\b", "ext")
|
||||
.GetFilePath()
|
||||
.Should().Be(@"C:\a\b\foobar.ext");
|
||||
|
||||
[TestMethod]
|
||||
public void IfSeries_no_series()
|
||||
=> Templates.getFileNamingTemplate(GetLibraryBook("asin", ""), "foo<if series->-<series>-<id>-<-if series>bar", @"C:\a\b", "ext")
|
||||
.GetFilePath()
|
||||
.Should().Be(@"C:\a\b\foobar.ext");
|
||||
|
||||
[TestMethod]
|
||||
public void IfSeries_with_series()
|
||||
=> Templates.getFileNamingTemplate(GetLibraryBook("asin", "Sherlock Holmes"), "foo<if series->-<series>-<id>-<-if series>bar", @"C:\a\b", "ext")
|
||||
.GetFilePath()
|
||||
.Should().Be(@"C:\a\b\foo-Sherlock Holmes-asin-bar.ext");
|
||||
}
|
||||
}
|
||||
|
||||
namespace Templates_Folder_Tests
|
||||
{
|
||||
[TestClass]
|
||||
public class GetErrors
|
||||
{
|
||||
[TestMethod]
|
||||
public void null_is_invalid() => Tests(null, new[] { Templates.ERROR_NULL_IS_INVALID });
|
||||
|
||||
[TestMethod]
|
||||
public void empty_is_valid() => valid_tests("");
|
||||
|
||||
[TestMethod]
|
||||
public void whitespace_is_valid() => valid_tests(" ");
|
||||
|
||||
[TestMethod]
|
||||
[DataRow(@"foo")]
|
||||
[DataRow(@"\foo")]
|
||||
[DataRow(@"foo\")]
|
||||
[DataRow(@"\foo\")]
|
||||
[DataRow(@"foo\bar")]
|
||||
[DataRow(@"<id>")]
|
||||
[DataRow(@"<id>\<title>")]
|
||||
public void valid_tests(string template) => Tests(template, Array.Empty<string>());
|
||||
|
||||
[TestMethod]
|
||||
[DataRow(@"C:\", Templates.ERROR_FULL_PATH_IS_INVALID)]
|
||||
public void Tests(string template, params string[] expected)
|
||||
{
|
||||
var result = Templates.Folder.GetErrors(template);
|
||||
result.Count().Should().Be(expected.Length);
|
||||
result.Should().BeEquivalentTo(expected);
|
||||
}
|
||||
}
|
||||
|
||||
[TestClass]
|
||||
public class IsValid
|
||||
{
|
||||
[TestMethod]
|
||||
public void null_is_invalid() => Tests(null, false);
|
||||
|
||||
[TestMethod]
|
||||
public void empty_is_valid() => Tests("", true);
|
||||
|
||||
[TestMethod]
|
||||
public void whitespace_is_valid() => Tests(" ", true);
|
||||
|
||||
[TestMethod]
|
||||
[DataRow(@"C:\", false)]
|
||||
[DataRow(@"foo", true)]
|
||||
[DataRow(@"\foo", true)]
|
||||
[DataRow(@"foo\", true)]
|
||||
[DataRow(@"\foo\", true)]
|
||||
[DataRow(@"foo\bar", true)]
|
||||
[DataRow(@"<id>", true)]
|
||||
[DataRow(@"<id>\<title>", true)]
|
||||
public void Tests(string template, bool expected) => Templates.Folder.IsValid(template).Should().Be(expected);
|
||||
}
|
||||
|
||||
[TestClass]
|
||||
public class GetWarnings
|
||||
{
|
||||
[TestMethod]
|
||||
public void null_is_invalid() => Tests(null, new[] { Templates.ERROR_NULL_IS_INVALID });
|
||||
|
||||
[TestMethod]
|
||||
public void empty_has_warnings() => Tests("", Templates.WARNING_EMPTY, Templates.WARNING_NO_TAGS);
|
||||
|
||||
[TestMethod]
|
||||
public void whitespace_has_warnings() => Tests(" ", Templates.WARNING_WHITE_SPACE, Templates.WARNING_NO_TAGS);
|
||||
|
||||
[TestMethod]
|
||||
[DataRow(@"<id>\foo\bar")]
|
||||
public void valid_tests(string template) => Tests(template, Array.Empty<string>());
|
||||
|
||||
[TestMethod]
|
||||
[DataRow(@"no tags", Templates.WARNING_NO_TAGS)]
|
||||
[DataRow("<ch#> <id>", Templates.WARNING_HAS_CHAPTER_TAGS)]
|
||||
[DataRow("<ch#> chapter tag", Templates.WARNING_NO_TAGS, Templates.WARNING_HAS_CHAPTER_TAGS)]
|
||||
public void Tests(string template, params string[] expected)
|
||||
{
|
||||
var result = Templates.Folder.GetWarnings(template);
|
||||
result.Count().Should().Be(expected.Length);
|
||||
result.Should().BeEquivalentTo(expected);
|
||||
}
|
||||
}
|
||||
|
||||
[TestClass]
|
||||
public class HasWarnings
|
||||
{
|
||||
[TestMethod]
|
||||
public void null_has_warnings() => Tests(null, true);
|
||||
|
||||
[TestMethod]
|
||||
public void empty_has_warnings() => Tests("", true);
|
||||
|
||||
[TestMethod]
|
||||
public void whitespace_has_warnings() => Tests(" ", true);
|
||||
|
||||
[TestMethod]
|
||||
[DataRow(@"no tags", true)]
|
||||
[DataRow(@"<id>\foo\bar", false)]
|
||||
[DataRow("<ch#> <id>", true)]
|
||||
[DataRow("<ch#> chapter tag", true)]
|
||||
public void Tests(string template, bool expected) => Templates.Folder.HasWarnings(template).Should().Be(expected);
|
||||
}
|
||||
|
||||
[TestClass]
|
||||
public class TagCount
|
||||
{
|
||||
[TestMethod]
|
||||
public void null_throws() => Assert.ThrowsException<NullReferenceException>(() => Templates.Folder.TagCount(null));
|
||||
|
||||
[TestMethod]
|
||||
public void empty() => Tests("", 0);
|
||||
|
||||
[TestMethod]
|
||||
public void whitespace() => Tests(" ", 0);
|
||||
|
||||
[TestMethod]
|
||||
[DataRow("no tags", 0)]
|
||||
[DataRow(@"<id>\foo\bar", 1)]
|
||||
[DataRow("<id> <id>", 2)]
|
||||
[DataRow("<id <id> >", 1)]
|
||||
[DataRow("<id> <title>", 2)]
|
||||
[DataRow("id> <title incomplete tags", 0)]
|
||||
[DataRow("<not a real tag>", 0)]
|
||||
[DataRow("<ch#> non-folder tag", 0)]
|
||||
[DataRow("<ID> case specific", 0)]
|
||||
public void Tests(string template, int expected) => Templates.Folder.TagCount(template).Should().Be(expected);
|
||||
}
|
||||
}
|
||||
|
||||
namespace Templates_File_Tests
|
||||
{
|
||||
[TestClass]
|
||||
public class GetErrors
|
||||
{
|
||||
[TestMethod]
|
||||
public void null_is_invalid() => Tests(null, new[] { Templates.ERROR_NULL_IS_INVALID });
|
||||
|
||||
[TestMethod]
|
||||
public void empty_is_valid() => valid_tests("");
|
||||
|
||||
[TestMethod]
|
||||
public void whitespace_is_valid() => valid_tests(" ");
|
||||
|
||||
[TestMethod]
|
||||
[DataRow(@"foo")]
|
||||
[DataRow(@"<id>")]
|
||||
public void valid_tests(string template) => Tests(template, Array.Empty<string>());
|
||||
|
||||
|
||||
[TestMethod]
|
||||
[DataRow(@"C:\", Templates.ERROR_INVALID_FILE_NAME_CHAR)]
|
||||
[DataRow(@"\foo", Templates.ERROR_INVALID_FILE_NAME_CHAR)]
|
||||
[DataRow(@"/foo", Templates.ERROR_INVALID_FILE_NAME_CHAR)]
|
||||
[DataRow(@"C:\", Templates.ERROR_INVALID_FILE_NAME_CHAR)]
|
||||
public void Tests(string template, params string[] expected)
|
||||
{
|
||||
var result = Templates.File.GetErrors(template);
|
||||
result.Count().Should().Be(expected.Length);
|
||||
result.Should().BeEquivalentTo(expected);
|
||||
}
|
||||
}
|
||||
|
||||
[TestClass]
|
||||
public class IsValid
|
||||
{
|
||||
[TestMethod]
|
||||
public void null_is_invalid() => Tests(null, false);
|
||||
|
||||
[TestMethod]
|
||||
public void empty_is_valid() => Tests("", true);
|
||||
|
||||
[TestMethod]
|
||||
public void whitespace_is_valid() => Tests(" ", true);
|
||||
|
||||
[TestMethod]
|
||||
[DataRow(@"C:\", false)]
|
||||
[DataRow(@"foo", true)]
|
||||
[DataRow(@"\foo", false)]
|
||||
[DataRow(@"/foo", false)]
|
||||
[DataRow(@"<id>", true)]
|
||||
public void Tests(string template, bool expected) => Templates.File.IsValid(template).Should().Be(expected);
|
||||
}
|
||||
|
||||
// same as Templates.Folder.GetWarnings
|
||||
//[TestClass]
|
||||
//public class GetWarnings { }
|
||||
|
||||
// same as Templates.Folder.HasWarnings
|
||||
//[TestClass]
|
||||
//public class HasWarnings { }
|
||||
|
||||
// same as Templates.Folder.TagCount
|
||||
//[TestClass]
|
||||
//public class TagCount { }
|
||||
}
|
||||
|
||||
namespace Templates_ChapterFile_Tests
|
||||
{
|
||||
// same as Templates.File.GetErrors
|
||||
//[TestClass]
|
||||
//public class GetErrors { }
|
||||
|
||||
// same as Templates.File.IsValid
|
||||
//[TestClass]
|
||||
//public class IsValid { }
|
||||
|
||||
[TestClass]
|
||||
public class GetWarnings
|
||||
{
|
||||
[TestMethod]
|
||||
public void null_is_invalid() => Tests(null, new[] { Templates.ERROR_NULL_IS_INVALID });
|
||||
|
||||
[TestMethod]
|
||||
public void empty_has_warnings() => Tests("", Templates.WARNING_EMPTY, Templates.WARNING_NO_TAGS, Templates.WARNING_NO_CHAPTER_NUMBER_TAG);
|
||||
|
||||
[TestMethod]
|
||||
public void whitespace_has_warnings() => Tests(" ", Templates.WARNING_WHITE_SPACE, Templates.WARNING_NO_TAGS, Templates.WARNING_NO_CHAPTER_NUMBER_TAG);
|
||||
|
||||
[TestMethod]
|
||||
[DataRow("<ch#>")]
|
||||
[DataRow("<ch#> <id>")]
|
||||
public void valid_tests(string template) => Tests(template, Array.Empty<string>());
|
||||
|
||||
[TestMethod]
|
||||
[DataRow(@"no tags", Templates.WARNING_NO_TAGS, Templates.WARNING_NO_CHAPTER_NUMBER_TAG)]
|
||||
[DataRow(@"<id>\foo\bar", Templates.ERROR_INVALID_FILE_NAME_CHAR, Templates.WARNING_NO_CHAPTER_NUMBER_TAG)]
|
||||
[DataRow("<chapter count> -- chapter tag but not ch# or ch_#", Templates.WARNING_NO_TAGS, Templates.WARNING_NO_CHAPTER_NUMBER_TAG)]
|
||||
public void Tests(string template, params string[] expected)
|
||||
{
|
||||
var result = Templates.ChapterFile.GetWarnings(template);
|
||||
result.Count().Should().Be(expected.Length);
|
||||
result.Should().BeEquivalentTo(expected);
|
||||
}
|
||||
}
|
||||
|
||||
[TestClass]
|
||||
public class HasWarnings
|
||||
{
|
||||
[TestMethod]
|
||||
public void null_has_warnings() => Tests(null, true);
|
||||
|
||||
[TestMethod]
|
||||
public void empty_has_warnings() => Tests("", true);
|
||||
|
||||
[TestMethod]
|
||||
public void whitespace_has_warnings() => Tests(" ", true);
|
||||
|
||||
[TestMethod]
|
||||
[DataRow(@"no tags", true)]
|
||||
[DataRow(@"<id>\foo\bar", true)]
|
||||
[DataRow("<ch#> <id>", false)]
|
||||
[DataRow("<ch#> -- chapter tag", false)]
|
||||
[DataRow("<chapter count> -- chapter tag but not ch# or ch_#", true)]
|
||||
public void Tests(string template, bool expected) => Templates.ChapterFile.HasWarnings(template).Should().Be(expected);
|
||||
}
|
||||
|
||||
[TestClass]
|
||||
public class TagCount
|
||||
{
|
||||
[TestMethod]
|
||||
public void null_is_not_recommended() => Assert.ThrowsException<NullReferenceException>(() => Tests(null, -1));
|
||||
|
||||
[TestMethod]
|
||||
public void empty_is_not_recommended() => Tests("", 0);
|
||||
|
||||
[TestMethod]
|
||||
public void whitespace_is_not_recommended() => Tests(" ", 0);
|
||||
|
||||
[TestMethod]
|
||||
[DataRow("no tags", 0)]
|
||||
[DataRow(@"<id>\foo\bar", 1)]
|
||||
[DataRow("<id> <id>", 2)]
|
||||
[DataRow("<id <id> >", 1)]
|
||||
[DataRow("<id> <title>", 2)]
|
||||
[DataRow("id> <title incomplete tags", 0)]
|
||||
[DataRow("<not a real tag>", 0)]
|
||||
[DataRow("<ch#> non-folder tag", 1)]
|
||||
[DataRow("<ID> case specific", 0)]
|
||||
public void Tests(string template, int expected) => Templates.ChapterFile.TagCount(template).Should().Be(expected);
|
||||
}
|
||||
|
||||
[TestClass]
|
||||
public class GetPortionFilename
|
||||
{
|
||||
[TestMethod]
|
||||
[DataRow("asin", "[<id>] <ch# 0> of <ch count> - <ch title>", @"C:\foo\", "txt", 6, 10, "chap", @"C:\foo\[asin] 06 of 10 - chap.txt")]
|
||||
[DataRow("asin", "<ch#>", @"C:\foo\", "txt", 6, 10, "chap", @"C:\foo\6.txt")]
|
||||
public void Tests(string asin, string template, string dir, string ext, int pos, int total, string chapter, string expected)
|
||||
=> Templates.ChapterFile.GetPortionFilename(GetLibraryBook(asin), template, new() { OutputFileName = $"xyz.{ext}", PartsPosition = pos, PartsTotal = total, Title = chapter }, dir)
|
||||
.Should().Be(expected);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0-windows</TargetFramework>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FluentAssertions" Version="6.6.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
|
||||
<PackageReference Include="Moq" Version="4.17.2" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="2.2.10" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="2.2.10" />
|
||||
<PackageReference Include="coverlet.collector" Version="3.1.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\LibationSearchEngine\LibationSearchEngine.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Dinah.Core;
|
||||
using FluentAssertions;
|
||||
using FluentAssertions.Common;
|
||||
using LibationSearchEngine;
|
||||
using Microsoft.VisualStudio.TestPlatform.Common.Filtering;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
using Moq.Protected;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace SearchEngineTests
|
||||
{
|
||||
[TestClass]
|
||||
public class FormatSearchQuery
|
||||
{
|
||||
[TestMethod]
|
||||
// null, empty, whitespace -- *:*
|
||||
[DataRow(null, "*:*")]
|
||||
[DataRow("", "*:*")]
|
||||
[DataRow(" ", "*:*")]
|
||||
|
||||
// tag surrounded by spaces
|
||||
[DataRow("[foo]", "tags:foo")]
|
||||
[DataRow(" [foo]", " tags:foo")]
|
||||
[DataRow("[foo] ", "tags:foo ")]
|
||||
[DataRow(" [foo] ", " tags:foo ")]
|
||||
[DataRow("-[foo]", "-tags:foo")]
|
||||
[DataRow(" -[foo]", " -tags:foo")]
|
||||
[DataRow("-[foo] ", "-tags:foo ")]
|
||||
[DataRow(" -[foo] ", " -tags:foo ")]
|
||||
|
||||
// tag case irrelevant
|
||||
[DataRow("[FoO]", "tags:FoO")]
|
||||
|
||||
// bool keyword surrounded by spaces
|
||||
[DataRow("israted", "israted:True")]
|
||||
[DataRow(" israted", " israted:True")]
|
||||
[DataRow("israted ", "israted:True ")]
|
||||
[DataRow(" israted ", " israted:True ")]
|
||||
[DataRow("-israted", "-israted:True")]
|
||||
[DataRow(" -israted", " -israted:True")]
|
||||
[DataRow("-israted ", "-israted:True ")]
|
||||
[DataRow(" -israted ", " -israted:True ")]
|
||||
|
||||
// bool keyword. Append :True
|
||||
[DataRow("israted", "israted:True")]
|
||||
|
||||
// bool keyword with [:bool]. Do not add :True
|
||||
[DataRow("israted:True", "israted:True")]
|
||||
[DataRow("isRated:false", "israted:false")]
|
||||
|
||||
// tag which happens to be a bool keyword >> parse as tag
|
||||
[DataRow("[israted]", "tags:israted")]
|
||||
|
||||
// numbers with "to". TO all caps, numbers [8.2] format
|
||||
[DataRow("1 to 10", "00000001.00 TO 00000010.00")]
|
||||
[DataRow("19990101 to 20001231", "19990101.00 TO 20001231.00")]
|
||||
|
||||
// field to lowercase
|
||||
[DataRow("Author:Doyle", "author:Doyle")]
|
||||
// bool field to lowercase
|
||||
[DataRow("IsRated", "israted:True")]
|
||||
[DataRow("-isRATED", "-israted:True")]
|
||||
|
||||
public void FormattingTest(string input, string output)
|
||||
=> SearchEngine.FormatSearchQuery(input).Should().Be(output);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue