Files
Umbraco-CMS/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Scoping/ScopeTests.cs

804 lines
32 KiB
C#
Raw Normal View History

// Copyright (c) Umbraco.
// See LICENSE for more details.
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Castle.Core.Logging;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Moq;
2017-05-12 14:49:44 +02:00
using NUnit.Framework;
using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Core.Scoping;
using Umbraco.Cms.Infrastructure.Persistence;
Examine 2.0 integration (#10241) * Init commit for examine 2.0 work, most old umb examine tests working, probably a lot that doesn't * Gets Umbraco Examine tests passing and makes some sense out of them, fixes some underlying issues. * Large refactor, remove TaskHelper, rename Notifications to be consistent, Gets all examine/lucene indexes building and startup ordered in the correct way, removes old files, creates new IUmbracoIndexingHandler for abstracting out all index operations for umbraco data, abstracts out IIndexRebuilder, Fixes Stack overflow with LiveModelsProvider and loading assemblies, ports some changes from v8 for startup handling with cold boots, refactors out LastSyncedFileManager * fix up issues with rebuilding and management dashboard. * removes old files, removes NetworkHelper, fixes LastSyncedFileManager implementation to ensure the machine name is used, fix up logging with cold boot state. * Makes MainDom safer to use and makes PublishedSnapshotService lazily register with MainDom * lazily acquire application id (fix unit tests) * Fixes resource casing and missing test file * Ensures caches when requiring internal services for PublishedSnapshotService, UseNuCache is a separate call, shouldn't be buried in AddWebComponents, was also causing issues in integration tests since nucache was being used for the Id2Key service. * For UmbracoTestServerTestBase enable nucache services * Fixing tests * Fix another test * Fixes tests, use TestHostingEnvironment, make Tests.Common use net5, remove old Lucene.Net.Contrib ref. * Fixes up some review notes * Fixes issue with doubly registering PublishedSnapshotService meanig there could be 2x instances of it * Checks for parseexception when executing the query * Use application root instead of duplicating functionality. * Added Examine project to netcore only solution file * Fixed casing issue with LazyLoad, that is not lowercase. * uses cancellationToken instead of bool flag, fixes always reading lastId from the LastSyncedFileManager, fixes RecurringHostedServiceBase so that there isn't an overlapping thread for the same task type * Fix tests * remove legacy test project from solution file * Fix test Co-authored-by: Bjarke Berg <mail@bergmania.dk>
2021-05-18 18:31:38 +10:00
using Umbraco.Cms.Tests.Common;
using Umbraco.Cms.Tests.Common.Testing;
using Umbraco.Cms.Tests.Integration.Testing;
using Umbraco.Extensions;
2017-05-12 14:49:44 +02:00
namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Scoping
2017-05-12 14:49:44 +02:00
{
[TestFixture]
[UmbracoTest(Database = UmbracoTestOptions.Database.NewEmptyPerFixture)]
public class ScopeTests : UmbracoIntegrationTest
2017-05-12 14:49:44 +02:00
{
private new ScopeProvider ScopeProvider => (ScopeProvider)base.ScopeProvider;
2017-05-12 14:49:44 +02:00
[SetUp]
public void SetUp() => Assert.IsNull(ScopeProvider.AmbientScope); // gone
2017-05-12 14:49:44 +02:00
protected override void ConfigureTestServices(IServiceCollection services)
{
// Need to have a mockable request cache for tests
var appCaches = new AppCaches(
NoAppCache.Instance,
Mock.Of<IRequestCache>(x => x.IsAvailable == false),
new IsolatedCaches(_ => NoAppCache.Instance));
services.AddUnique(appCaches);
}
[Test]
public void GivenUncompletedScopeOnChildThread_WhenTheParentCompletes_TheTransactionIsRolledBack()
{
ScopeProvider scopeProvider = ScopeProvider;
Assert.IsNull(ScopeProvider.AmbientScope);
IScope mainScope = scopeProvider.CreateScope();
2021-03-04 23:59:41 +11:00
var t = Task.Run(() =>
{
IScope nested = scopeProvider.CreateScope();
Thread.Sleep(2000);
nested.Dispose();
2021-03-04 23:59:41 +11:00
});
Thread.Sleep(1000); // mimic some long running operation that is shorter than the other thread
mainScope.Complete();
Assert.Throws<InvalidOperationException>(() => mainScope.Dispose());
2021-03-04 23:59:41 +11:00
Task.WaitAll(t);
}
[Test]
public void GivenNonDisposedChildScope_WhenTheParentDisposes_ThenInvalidOperationExceptionThrows()
{
// this all runs in the same execution context so the AmbientScope reference isn't a copy
ScopeProvider scopeProvider = ScopeProvider;
Assert.IsNull(ScopeProvider.AmbientScope);
IScope mainScope = scopeProvider.CreateScope();
IScope nested = scopeProvider.CreateScope(); // not disposing
InvalidOperationException ex = Assert.Throws<InvalidOperationException>(() => mainScope.Dispose());
Console.WriteLine(ex);
}
[Test]
public void GivenChildThread_WhenParentDisposedBeforeChild_ParentScopeThrows()
2021-03-04 23:59:41 +11:00
{
ScopeProvider scopeProvider = ScopeProvider;
Assert.IsNull(ScopeProvider.AmbientScope);
IScope mainScope = scopeProvider.CreateScope();
var t = Task.Run(() =>
{
Console.WriteLine("Child Task start: " + scopeProvider.AmbientScope.InstanceId);
// This will push the child scope to the top of the Stack
2021-03-04 23:59:41 +11:00
IScope nested = scopeProvider.CreateScope();
Console.WriteLine("Child Task scope created: " + scopeProvider.AmbientScope.InstanceId);
Thread.Sleep(5000); // block for a bit to ensure the parent task is disposed first
2021-03-04 23:59:41 +11:00
Console.WriteLine("Child Task before dispose: " + scopeProvider.AmbientScope.InstanceId);
nested.Dispose();
Console.WriteLine("Child Task after dispose: " + scopeProvider.AmbientScope.InstanceId);
2021-03-04 23:59:41 +11:00
});
// provide some time for the child thread to start so the ambient context is copied in AsyncLocal
Thread.Sleep(2000);
2021-03-04 23:59:41 +11:00
// now dispose the main without waiting for the child thread to join
Console.WriteLine("Parent Task disposing: " + scopeProvider.AmbientScope.InstanceId);
// This will throw because at this stage a child scope has been created which means
// it is the Ambient (top) scope but here we're trying to dispose the non top scope.
Assert.Throws<InvalidOperationException>(() => mainScope.Dispose());
Task.WaitAll(t); // wait for the child to dispose
mainScope.Dispose(); // now it's ok
2021-03-04 23:59:41 +11:00
Console.WriteLine("Parent Task disposed: " + scopeProvider.AmbientScope?.InstanceId);
}
[Test]
public void GivenChildThread_WhenChildDisposedBeforeParent_OK()
{
ScopeProvider scopeProvider = ScopeProvider;
Assert.IsNull(ScopeProvider.AmbientScope);
IScope mainScope = scopeProvider.CreateScope();
// Task.Run will flow the execution context unless ExecutionContext.SuppressFlow() is explicitly called.
// This is what occurs in normal async behavior since it is expected to await (and join) the main thread,
// but if Task.Run is used as a fire and forget thread without being done correctly then the Scope will
// flow to that thread.
var t = Task.Run(() =>
2021-03-04 23:59:41 +11:00
{
Console.WriteLine("Child Task start: " + scopeProvider.AmbientScope.InstanceId);
IScope nested = scopeProvider.CreateScope();
2021-03-04 23:59:41 +11:00
Console.WriteLine("Child Task before dispose: " + scopeProvider.AmbientScope.InstanceId);
nested.Dispose();
Console.WriteLine("Child Task after disposed: " + scopeProvider.AmbientScope.InstanceId);
});
2021-03-09 17:50:43 +11:00
Console.WriteLine("Parent Task waiting: " + scopeProvider.AmbientScope?.InstanceId);
Task.WaitAll(t);
2021-03-04 23:59:41 +11:00
Console.WriteLine("Parent Task disposing: " + scopeProvider.AmbientScope.InstanceId);
mainScope.Dispose();
Console.WriteLine("Parent Task disposed: " + scopeProvider.AmbientScope?.InstanceId);
2021-03-04 23:59:41 +11:00
Assert.Pass();
}
2017-05-12 14:49:44 +02:00
[Test]
public void SimpleCreateScope()
{
ScopeProvider scopeProvider = ScopeProvider;
2017-05-12 14:49:44 +02:00
Assert.IsNull(ScopeProvider.AmbientScope);
using (IScope scope = scopeProvider.CreateScope())
2017-05-12 14:49:44 +02:00
{
Assert.IsInstanceOf<Scope>(scope);
Assert.IsNotNull(scopeProvider.AmbientScope);
Assert.AreSame(scope, scopeProvider.AmbientScope);
}
2017-05-12 14:49:44 +02:00
Assert.IsNull(scopeProvider.AmbientScope);
}
[Test]
public void SimpleCreateScopeContext()
{
ScopeProvider scopeProvider = ScopeProvider;
2017-05-12 14:49:44 +02:00
Assert.IsNull(scopeProvider.AmbientScope);
using (IScope scope = scopeProvider.CreateScope())
2017-05-12 14:49:44 +02:00
{
Assert.IsInstanceOf<Scope>(scope);
Assert.IsNotNull(scopeProvider.AmbientScope);
Assert.AreSame(scope, scopeProvider.AmbientScope);
Assert.IsNotNull(scopeProvider.AmbientContext);
Assert.IsNotNull(scopeProvider.Context);
}
2017-05-12 14:49:44 +02:00
Assert.IsNull(scopeProvider.AmbientScope);
Assert.IsNull(scopeProvider.AmbientContext);
}
[Test]
public void SimpleCreateScopeDatabase()
{
ScopeProvider scopeProvider = ScopeProvider;
2017-05-12 14:49:44 +02:00
IUmbracoDatabase database;
Assert.IsNull(scopeProvider.AmbientScope);
using (IScope scope = scopeProvider.CreateScope())
2017-05-12 14:49:44 +02:00
{
Assert.IsInstanceOf<Scope>(scope);
Assert.IsNotNull(scopeProvider.AmbientScope);
Assert.AreSame(scope, scopeProvider.AmbientScope);
2022-01-13 17:44:11 +00:00
database = ScopeAccessor.AmbientScope.Database; // populates scope's database
2017-05-12 14:49:44 +02:00
Assert.IsNotNull(database);
Assert.IsNotNull(database.Connection); // in a transaction
}
2017-05-12 14:49:44 +02:00
Assert.IsNull(scopeProvider.AmbientScope);
Assert.IsNull(database.Connection); // poof gone
}
[Test]
public void NestedCreateScope()
{
ScopeProvider scopeProvider = ScopeProvider;
2017-05-12 14:49:44 +02:00
Assert.IsNull(scopeProvider.AmbientScope);
using (IScope scope = scopeProvider.CreateScope())
2017-05-12 14:49:44 +02:00
{
Assert.IsInstanceOf<Scope>(scope);
Assert.IsNotNull(scopeProvider.AmbientScope);
Assert.AreSame(scope, scopeProvider.AmbientScope);
using (IScope nested = scopeProvider.CreateScope())
2017-05-12 14:49:44 +02:00
{
Assert.IsInstanceOf<Scope>(nested);
Assert.IsNotNull(scopeProvider.AmbientScope);
Assert.AreSame(nested, scopeProvider.AmbientScope);
Assert.AreSame(scope, ((Scope)nested).ParentScope);
}
}
2017-05-12 14:49:44 +02:00
Assert.IsNull(scopeProvider.AmbientScope);
}
[Test]
public void NestedMigrateScope()
{
// Get the request cache mock and re-configure it to be available and used
var requestCacheDictionary = new Dictionary<string, object>();
IRequestCache requestCache = AppCaches.RequestCache;
var requestCacheMock = Mock.Get(requestCache);
requestCacheMock
.Setup(x => x.IsAvailable)
.Returns(true);
requestCacheMock
.Setup(x => x.Set(It.IsAny<string>(), It.IsAny<object>()))
.Returns((string key, object val) =>
{
requestCacheDictionary.Add(key, val);
return true;
});
requestCacheMock
.Setup(x => x.Get(It.IsAny<string>()))
.Returns((string key) => requestCacheDictionary.TryGetValue(key, out var val) ? val : null);
ScopeProvider scopeProvider = ScopeProvider;
2017-05-12 14:49:44 +02:00
Assert.IsNull(scopeProvider.AmbientScope);
using (IScope scope = scopeProvider.CreateScope())
2017-05-12 14:49:44 +02:00
{
Assert.IsInstanceOf<Scope>(scope);
Assert.IsNotNull(scopeProvider.AmbientScope);
Assert.AreSame(scope, scopeProvider.AmbientScope);
using (IScope nested = scopeProvider.CreateScope(callContext: true))
2017-05-12 14:49:44 +02:00
{
Assert.IsInstanceOf<Scope>(nested);
2017-05-12 14:49:44 +02:00
Assert.IsNotNull(scopeProvider.AmbientScope);
Assert.AreSame(nested, scopeProvider.AmbientScope);
Assert.AreSame(scope, ((Scope)nested).ParentScope);
2017-05-12 14:49:44 +02:00
// it's moved over to call context
ConcurrentStack<IScope> callContextScope = scopeProvider.GetCallContextScopeValue();
2019-12-23 16:04:17 +11:00
Assert.IsNotNull(callContextScope);
Assert.AreEqual(2, callContextScope.Count);
2017-05-12 14:49:44 +02:00
}
// it's naturally back in http context
2017-05-12 14:49:44 +02:00
}
Assert.IsNull(scopeProvider.AmbientScope);
2017-05-12 14:49:44 +02:00
}
[Test]
public void NestedCreateScopeContext()
{
ScopeProvider scopeProvider = ScopeProvider;
2017-05-12 14:49:44 +02:00
Assert.IsNull(scopeProvider.AmbientScope);
using (IScope scope = scopeProvider.CreateScope())
2017-05-12 14:49:44 +02:00
{
Assert.IsInstanceOf<Scope>(scope);
Assert.IsNotNull(scopeProvider.AmbientScope);
Assert.AreSame(scope, scopeProvider.AmbientScope);
Assert.IsNotNull(scopeProvider.AmbientContext);
IScopeContext context;
using (IScope nested = scopeProvider.CreateScope())
2017-05-12 14:49:44 +02:00
{
Assert.IsInstanceOf<Scope>(nested);
Assert.IsNotNull(scopeProvider.AmbientScope);
Assert.AreSame(nested, scopeProvider.AmbientScope);
Assert.AreSame(scope, ((Scope)nested).ParentScope);
2017-05-12 14:49:44 +02:00
Assert.IsNotNull(scopeProvider.Context);
Assert.IsNotNull(scopeProvider.AmbientContext);
context = scopeProvider.Context;
}
Assert.IsNotNull(scopeProvider.AmbientContext);
Assert.AreSame(context, scopeProvider.AmbientContext);
}
2017-05-12 14:49:44 +02:00
Assert.IsNull(scopeProvider.AmbientScope);
Assert.IsNull(scopeProvider.AmbientContext);
}
[Test]
public void NestedCreateScopeInnerException()
{
ScopeProvider scopeProvider = ScopeProvider;
2017-05-12 14:49:44 +02:00
bool? scopeCompleted = null;
Assert.IsNull(scopeProvider.AmbientScope);
try
{
using (IScope scope = scopeProvider.CreateScope())
2017-05-12 14:49:44 +02:00
{
scopeProvider.Context.Enlist("test", completed => scopeCompleted = completed);
Assert.IsInstanceOf<Scope>(scope);
Assert.IsNotNull(scopeProvider.AmbientScope);
Assert.AreSame(scope, scopeProvider.AmbientScope);
using (IScope nested = scopeProvider.CreateScope())
2017-05-12 14:49:44 +02:00
{
Assert.IsInstanceOf<Scope>(nested);
Assert.IsNotNull(scopeProvider.AmbientScope);
Assert.AreSame(nested, scopeProvider.AmbientScope);
Assert.AreSame(scope, ((Scope)nested).ParentScope);
2017-05-12 14:49:44 +02:00
nested.Complete();
throw new Exception("bang!");
}
2017-05-12 14:49:44 +02:00
scope.Complete();
}
2017-05-12 14:49:44 +02:00
Assert.Fail("Expected exception.");
}
catch (Exception e)
{
if (e.Message != "bang!")
{
2017-05-12 14:49:44 +02:00
Assert.Fail("Wrong exception.");
}
2017-05-12 14:49:44 +02:00
}
2017-05-12 14:49:44 +02:00
Assert.IsNull(scopeProvider.AmbientScope);
Assert.IsNotNull(scopeCompleted);
Assert.IsFalse(scopeCompleted.Value);
}
[Test]
public void NestedCreateScopeDatabase()
{
ScopeProvider scopeProvider = ScopeProvider;
2017-05-12 14:49:44 +02:00
IUmbracoDatabase database;
Assert.IsNull(scopeProvider.AmbientScope);
using (IScope scope = scopeProvider.CreateScope())
2017-05-12 14:49:44 +02:00
{
Assert.IsInstanceOf<Scope>(scope);
Assert.IsNotNull(scopeProvider.AmbientScope);
Assert.AreSame(scope, scopeProvider.AmbientScope);
2022-01-13 17:44:11 +00:00
database = ScopeAccessor.AmbientScope.Database; // populates scope's database
2017-05-12 14:49:44 +02:00
Assert.IsNotNull(database);
Assert.IsNotNull(database.Connection); // in a transaction
using (IScope nested = scopeProvider.CreateScope())
2017-05-12 14:49:44 +02:00
{
Assert.IsInstanceOf<Scope>(nested);
Assert.IsNotNull(scopeProvider.AmbientScope);
Assert.AreSame(nested, scopeProvider.AmbientScope);
Assert.AreSame(scope, ((Scope)nested).ParentScope);
2022-01-13 17:44:11 +00:00
Assert.AreSame(database, ScopeAccessor.AmbientScope.Database);
2017-05-12 14:49:44 +02:00
}
2017-05-12 14:49:44 +02:00
Assert.IsNotNull(database.Connection); // still
}
2017-05-12 14:49:44 +02:00
Assert.IsNull(scopeProvider.AmbientScope);
Assert.IsNull(database.Connection); // poof gone
}
[Test]
public void Transaction()
{
ScopeProvider scopeProvider = ScopeProvider;
2017-05-12 14:49:44 +02:00
using (IScope scope = scopeProvider.CreateScope())
2017-05-12 14:49:44 +02:00
{
2022-01-13 17:44:11 +00:00
ScopeAccessor.AmbientScope.Database.Execute("CREATE TABLE tmp3 (id INT, name NVARCHAR(64))");
2017-05-12 14:49:44 +02:00
scope.Complete();
}
using (IScope scope = scopeProvider.CreateScope())
2017-05-12 14:49:44 +02:00
{
2022-01-13 17:44:11 +00:00
ScopeAccessor.AmbientScope.Database.Execute("INSERT INTO tmp3 (id, name) VALUES (1, 'a')");
string n = ScopeAccessor.AmbientScope.Database.ExecuteScalar<string>("SELECT name FROM tmp3 WHERE id=1");
2017-05-12 14:49:44 +02:00
Assert.AreEqual("a", n);
}
using (IScope scope = scopeProvider.CreateScope())
2017-05-12 14:49:44 +02:00
{
2022-01-13 17:44:11 +00:00
string n = ScopeAccessor.AmbientScope.Database.ExecuteScalar<string>("SELECT name FROM tmp3 WHERE id=1");
2017-05-12 14:49:44 +02:00
Assert.IsNull(n);
}
using (IScope scope = scopeProvider.CreateScope())
2017-05-12 14:49:44 +02:00
{
2022-01-13 17:44:11 +00:00
ScopeAccessor.AmbientScope.Database.Execute("INSERT INTO tmp3 (id, name) VALUES (1, 'a')");
2017-05-12 14:49:44 +02:00
scope.Complete();
}
using (IScope scope = scopeProvider.CreateScope())
2017-05-12 14:49:44 +02:00
{
2022-01-13 17:44:11 +00:00
string n = ScopeAccessor.AmbientScope.Database.ExecuteScalar<string>("SELECT name FROM tmp3 WHERE id=1");
2017-05-12 14:49:44 +02:00
Assert.AreEqual("a", n);
}
}
[Test]
public void NestedTransactionInnerFail()
{
ScopeProvider scopeProvider = ScopeProvider;
2017-05-12 14:49:44 +02:00
using (IScope scope = scopeProvider.CreateScope())
2017-05-12 14:49:44 +02:00
{
2022-01-13 17:44:11 +00:00
ScopeAccessor.AmbientScope.Database.Execute($"CREATE TABLE tmp1 (id INT, name NVARCHAR(64))");
2017-05-12 14:49:44 +02:00
scope.Complete();
}
using (IScope scope = scopeProvider.CreateScope())
2017-05-12 14:49:44 +02:00
{
2022-01-13 17:44:11 +00:00
ScopeAccessor.AmbientScope.Database.Execute("INSERT INTO tmp1 (id, name) VALUES (1, 'a')");
string n = ScopeAccessor.AmbientScope.Database.ExecuteScalar<string>("SELECT name FROM tmp1 WHERE id=1");
2017-05-12 14:49:44 +02:00
Assert.AreEqual("a", n);
using (IScope nested = scopeProvider.CreateScope())
2017-05-12 14:49:44 +02:00
{
2022-01-13 17:44:11 +00:00
ScopeAccessor.AmbientScope.Database.Execute("INSERT INTO tmp1 (id, name) VALUES (2, 'b')");
string nn = ScopeAccessor.AmbientScope.Database.ExecuteScalar<string>("SELECT name FROM tmp1 WHERE id=2");
2017-05-12 14:49:44 +02:00
Assert.AreEqual("b", nn);
}
2022-01-13 17:44:11 +00:00
n = ScopeAccessor.AmbientScope.Database.ExecuteScalar<string>("SELECT name FROM tmp1 WHERE id=2");
2017-05-12 14:49:44 +02:00
Assert.AreEqual("b", n);
scope.Complete();
}
using (IScope scope = scopeProvider.CreateScope())
2017-05-12 14:49:44 +02:00
{
2022-01-13 17:44:11 +00:00
string n = ScopeAccessor.AmbientScope.Database.ExecuteScalar<string>("SELECT name FROM tmp1 WHERE id=1");
2017-05-12 14:49:44 +02:00
Assert.IsNull(n);
2022-01-13 17:44:11 +00:00
n = ScopeAccessor.AmbientScope.Database.ExecuteScalar<string>("SELECT name FROM tmp1 WHERE id=2");
2017-05-12 14:49:44 +02:00
Assert.IsNull(n);
}
}
[Test]
public void NestedTransactionOuterFail()
{
ScopeProvider scopeProvider = ScopeProvider;
2017-05-12 14:49:44 +02:00
using (IScope scope = scopeProvider.CreateScope())
2017-05-12 14:49:44 +02:00
{
2022-01-13 17:44:11 +00:00
ScopeAccessor.AmbientScope.Database.Execute("CREATE TABLE tmp2 (id INT, name NVARCHAR(64))");
2017-05-12 14:49:44 +02:00
scope.Complete();
}
using (IScope scope = scopeProvider.CreateScope())
2017-05-12 14:49:44 +02:00
{
2022-01-13 17:44:11 +00:00
ScopeAccessor.AmbientScope.Database.Execute("INSERT INTO tmp2 (id, name) VALUES (1, 'a')");
string n = ScopeAccessor.AmbientScope.Database.ExecuteScalar<string>("SELECT name FROM tmp2 WHERE id=1");
2017-05-12 14:49:44 +02:00
Assert.AreEqual("a", n);
using (IScope nested = scopeProvider.CreateScope())
2017-05-12 14:49:44 +02:00
{
2022-01-13 17:44:11 +00:00
ScopeAccessor.AmbientScope.Database.Execute("INSERT INTO tmp2 (id, name) VALUES (2, 'b')");
string nn = ScopeAccessor.AmbientScope.Database.ExecuteScalar<string>("SELECT name FROM tmp2 WHERE id=2");
2017-05-12 14:49:44 +02:00
Assert.AreEqual("b", nn);
nested.Complete();
}
2022-01-13 17:44:11 +00:00
n = ScopeAccessor.AmbientScope.Database.ExecuteScalar<string>("SELECT name FROM tmp2 WHERE id=2");
2017-05-12 14:49:44 +02:00
Assert.AreEqual("b", n);
}
using (IScope scope = scopeProvider.CreateScope())
2017-05-12 14:49:44 +02:00
{
2022-01-13 17:44:11 +00:00
string n = ScopeAccessor.AmbientScope.Database.ExecuteScalar<string>("SELECT name FROM tmp2 WHERE id=1");
2017-05-12 14:49:44 +02:00
Assert.IsNull(n);
2022-01-13 17:44:11 +00:00
n = ScopeAccessor.AmbientScope.Database.ExecuteScalar<string>("SELECT name FROM tmp2 WHERE id=2");
2017-05-12 14:49:44 +02:00
Assert.IsNull(n);
}
}
[Test]
public void NestedTransactionComplete()
{
ScopeProvider scopeProvider = ScopeProvider;
2017-05-12 14:49:44 +02:00
using (IScope scope = scopeProvider.CreateScope())
2017-05-12 14:49:44 +02:00
{
2022-01-13 17:44:11 +00:00
ScopeAccessor.AmbientScope.Database.Execute("CREATE TABLE tmp (id INT, name NVARCHAR(64))");
2017-05-12 14:49:44 +02:00
scope.Complete();
}
using (IScope scope = scopeProvider.CreateScope())
2017-05-12 14:49:44 +02:00
{
2022-01-13 17:44:11 +00:00
ScopeAccessor.AmbientScope.Database.Execute("INSERT INTO tmp (id, name) VALUES (1, 'a')");
string n = ScopeAccessor.AmbientScope.Database.ExecuteScalar<string>("SELECT name FROM tmp WHERE id=1");
2017-05-12 14:49:44 +02:00
Assert.AreEqual("a", n);
using (IScope nested = scopeProvider.CreateScope())
2017-05-12 14:49:44 +02:00
{
2022-01-13 17:44:11 +00:00
ScopeAccessor.AmbientScope.Database.Execute("INSERT INTO tmp (id, name) VALUES (2, 'b')");
string nn = ScopeAccessor.AmbientScope.Database.ExecuteScalar<string>("SELECT name FROM tmp WHERE id=2");
2017-05-12 14:49:44 +02:00
Assert.AreEqual("b", nn);
nested.Complete();
}
2022-01-13 17:44:11 +00:00
n = ScopeAccessor.AmbientScope.Database.ExecuteScalar<string>("SELECT name FROM tmp WHERE id=2");
2017-05-12 14:49:44 +02:00
Assert.AreEqual("b", n);
scope.Complete();
}
using (IScope scope = scopeProvider.CreateScope())
2017-05-12 14:49:44 +02:00
{
2022-01-13 17:44:11 +00:00
string n = ScopeAccessor.AmbientScope.Database.ExecuteScalar<string>("SELECT name FROM tmp WHERE id=1");
2017-05-12 14:49:44 +02:00
Assert.AreEqual("a", n);
2022-01-13 17:44:11 +00:00
n = ScopeAccessor.AmbientScope.Database.ExecuteScalar<string>("SELECT name FROM tmp WHERE id=2");
2017-05-12 14:49:44 +02:00
Assert.AreEqual("b", n);
}
}
[Test]
public void CallContextScope1()
{
var taskHelper = new TaskHelper(Mock.Of<ILogger<TaskHelper>>());
ScopeProvider scopeProvider = ScopeProvider;
using (IScope scope = scopeProvider.CreateScope())
2017-05-12 14:49:44 +02:00
{
Assert.IsNotNull(scopeProvider.AmbientScope);
Assert.IsNotNull(scopeProvider.AmbientContext);
// Run on another thread without a flowed context
2021-03-03 14:12:14 +11:00
Task t = taskHelper.ExecuteBackgroundTask(() =>
2017-05-12 14:49:44 +02:00
{
Assert.IsNull(scopeProvider.AmbientScope);
Assert.IsNull(scopeProvider.AmbientContext);
using (IScope newScope = scopeProvider.CreateScope())
2017-05-12 14:49:44 +02:00
{
Assert.IsNotNull(scopeProvider.AmbientScope);
Assert.IsNull(scopeProvider.AmbientScope.ParentScope);
Assert.IsNotNull(scopeProvider.AmbientContext);
}
Assert.IsNull(scopeProvider.AmbientScope);
Assert.IsNull(scopeProvider.AmbientContext);
return Task.CompletedTask;
});
Task.WaitAll(t);
2017-05-12 14:49:44 +02:00
Assert.IsNotNull(scopeProvider.AmbientScope);
Assert.AreSame(scope, scopeProvider.AmbientScope);
}
Assert.IsNull(scopeProvider.AmbientScope);
Assert.IsNull(scopeProvider.AmbientContext);
}
[Test]
public void CallContextScope2()
{
var taskHelper = new TaskHelper(Mock.Of<ILogger<TaskHelper>>());
ScopeProvider scopeProvider = ScopeProvider;
2017-05-12 14:49:44 +02:00
Assert.IsNull(scopeProvider.AmbientScope);
using (IScope scope = scopeProvider.CreateScope())
2017-05-12 14:49:44 +02:00
{
2019-12-03 08:43:09 +01:00
Assert.IsNotNull(scopeProvider.AmbientScope);
Assert.IsNotNull(scopeProvider.AmbientContext);
// Run on another thread without a flowed context
2021-03-03 14:12:14 +11:00
Task t = taskHelper.ExecuteBackgroundTask(() =>
2017-05-12 14:49:44 +02:00
{
2019-12-03 08:43:09 +01:00
Assert.IsNull(scopeProvider.AmbientScope);
Assert.IsNull(scopeProvider.AmbientContext);
2017-05-12 14:49:44 +02:00
using (IScope newScope = scopeProvider.CreateScope())
2019-12-03 08:43:09 +01:00
{
Assert.IsNotNull(scopeProvider.AmbientScope);
Assert.IsNull(scopeProvider.AmbientScope.ParentScope);
Assert.IsNotNull(scopeProvider.AmbientContext);
}
2017-05-12 14:49:44 +02:00
2019-12-03 08:43:09 +01:00
Assert.IsNull(scopeProvider.AmbientScope);
Assert.IsNull(scopeProvider.AmbientContext);
return Task.CompletedTask;
});
Task.WaitAll(t);
2019-12-03 08:43:09 +01:00
Assert.IsNotNull(scopeProvider.AmbientScope);
Assert.AreSame(scope, scopeProvider.AmbientScope);
}
2017-05-12 14:49:44 +02:00
2019-12-03 08:43:09 +01:00
Assert.IsNull(scopeProvider.AmbientScope);
Assert.IsNull(scopeProvider.AmbientContext);
2017-05-12 14:49:44 +02:00
}
[Test]
public void ScopeReference()
{
ScopeProvider scopeProvider = ScopeProvider;
2022-01-13 17:44:11 +00:00
Scope scope = (Scope) scopeProvider.CreateScope();
Scope nested = (Scope) scopeProvider.CreateScope();
2017-05-12 14:49:44 +02:00
Assert.IsNotNull(scopeProvider.AmbientScope);
2022-01-13 17:44:11 +00:00
var scopeRef = new HttpScopeReference(scopeProvider);
2021-03-05 15:37:34 +11:00
scopeRef.Register();
2017-05-12 14:49:44 +02:00
scopeRef.Dispose();
2022-01-13 17:44:11 +00:00
2017-05-12 14:49:44 +02:00
Assert.IsNull(scopeProvider.AmbientScope);
2022-01-13 17:44:11 +00:00
2017-05-12 14:49:44 +02:00
Assert.Throws<ObjectDisposedException>(() =>
{
IUmbracoDatabase db = scope.Database;
2017-05-12 14:49:44 +02:00
});
Assert.Throws<ObjectDisposedException>(() =>
{
IUmbracoDatabase db = nested.Database;
2017-05-12 14:49:44 +02:00
});
}
[TestCase(true)]
[TestCase(false)]
public void ScopeContextEnlist(bool complete)
{
ScopeProvider scopeProvider = ScopeProvider;
2017-05-12 14:49:44 +02:00
bool? completed = null;
IScope ambientScope = null;
IScopeContext ambientContext = null;
2017-05-12 14:49:44 +02:00
Assert.IsNull(scopeProvider.AmbientScope);
using (IScope scope = scopeProvider.CreateScope())
2017-05-12 14:49:44 +02:00
{
scopeProvider.Context.Enlist("name", c =>
{
completed = c;
ambientScope = scopeProvider.AmbientScope;
ambientContext = scopeProvider.AmbientContext;
});
if (complete)
{
2017-05-12 14:49:44 +02:00
scope.Complete();
}
2017-05-12 14:49:44 +02:00
}
2017-05-12 14:49:44 +02:00
Assert.IsNull(scopeProvider.AmbientScope);
Assert.IsNull(scopeProvider.AmbientContext);
Assert.IsNotNull(completed);
Assert.AreEqual(complete, completed.Value);
Assert.IsNull(ambientScope); // the scope is gone
Assert.IsNotNull(ambientContext); // the context is still there
}
2017-05-30 10:50:09 +02:00
[TestCase(true)]
[TestCase(false)]
public void ScopeContextEnlistAgain(bool complete)
{
ScopeProvider scopeProvider = ScopeProvider;
2017-05-30 10:50:09 +02:00
bool? completed = null;
bool? completed2 = null;
2017-05-30 10:50:09 +02:00
Assert.IsNull(scopeProvider.AmbientScope);
using (IScope scope = scopeProvider.CreateScope())
2017-05-30 10:50:09 +02:00
{
scopeProvider.Context.Enlist("name", c =>
{
completed = c;
// at that point the scope is gone, but the context is still there
IScopeContext ambientContext = scopeProvider.AmbientContext;
ambientContext.Enlist("another", c2 => completed2 = c2);
2017-05-30 10:50:09 +02:00
});
if (complete)
{
2017-05-30 10:50:09 +02:00
scope.Complete();
}
2017-05-30 10:50:09 +02:00
}
2017-05-30 10:50:09 +02:00
Assert.IsNull(scopeProvider.AmbientScope);
Assert.IsNull(scopeProvider.AmbientContext);
Assert.IsNotNull(completed);
Assert.AreEqual(complete, completed.Value);
Assert.AreEqual(complete, completed2.Value);
2017-05-30 10:50:09 +02:00
}
2017-05-12 14:49:44 +02:00
[Test]
public void ScopeContextException()
{
ScopeProvider scopeProvider = ScopeProvider;
2017-05-12 14:49:44 +02:00
bool? completed = null;
Assert.IsNull(scopeProvider.AmbientScope);
using (IScope scope = scopeProvider.CreateScope())
2017-05-12 14:49:44 +02:00
{
IScope detached = scopeProvider.CreateDetachedScope();
2017-05-12 14:49:44 +02:00
scopeProvider.AttachScope(detached);
2017-05-12 14:49:44 +02:00
// the exception does not prevent other enlisted items to run
// *and* it does not prevent the scope from properly going down
scopeProvider.Context.Enlist("name", c => throw new Exception("bang"));
scopeProvider.Context.Enlist("other", c => completed = c);
2017-05-12 14:49:44 +02:00
detached.Complete();
Assert.Throws<AggregateException>(() => detached.Dispose());
2017-05-12 14:49:44 +02:00
// even though disposing of the scope has thrown, it has exited
// properly ie it has removed itself, and the app remains clean
Assert.AreSame(scope, scopeProvider.AmbientScope);
scope.Complete();
}
2017-05-12 14:49:44 +02:00
Assert.IsNull(scopeProvider.AmbientScope);
Assert.IsNull(scopeProvider.AmbientContext);
Assert.IsNotNull(completed);
Assert.AreEqual(true, completed);
}
[Test]
public void DetachableScope()
{
ScopeProvider scopeProvider = ScopeProvider;
2017-05-12 14:49:44 +02:00
Assert.IsNull(scopeProvider.AmbientScope);
using (IScope scope = scopeProvider.CreateScope())
2017-05-12 14:49:44 +02:00
{
Assert.IsInstanceOf<Scope>(scope);
Assert.IsNotNull(scopeProvider.AmbientScope);
Assert.AreSame(scope, scopeProvider.AmbientScope);
Assert.IsNotNull(scopeProvider.AmbientContext); // the ambient context
Assert.IsNotNull(scopeProvider.Context); // the ambient context too (getter only)
IScopeContext context = scopeProvider.Context;
2017-05-12 14:49:44 +02:00
IScope detached = scopeProvider.CreateDetachedScope();
2017-05-12 14:49:44 +02:00
scopeProvider.AttachScope(detached);
Assert.AreEqual(detached, scopeProvider.AmbientScope);
Assert.AreNotSame(context, scopeProvider.Context);
// nesting under detached!
using (IScope nested = scopeProvider.CreateScope())
2017-05-12 14:49:44 +02:00
{
Assert.Throws<InvalidOperationException>(() =>
2017-05-12 14:49:44 +02:00
// cannot detach a non-detachable scope
scopeProvider.DetachScope());
2017-05-12 14:49:44 +02:00
nested.Complete();
}
Assert.AreEqual(detached, scopeProvider.AmbientScope);
Assert.AreNotSame(context, scopeProvider.Context);
// can detach
Assert.AreSame(detached, scopeProvider.DetachScope());
Assert.AreSame(scope, scopeProvider.AmbientScope);
Assert.AreSame(context, scopeProvider.AmbientContext);
Assert.Throws<InvalidOperationException>(() =>
2017-05-12 14:49:44 +02:00
// cannot disposed a non-attached scope
// in fact, only the ambient scope can be disposed
detached.Dispose());
2017-05-12 14:49:44 +02:00
scopeProvider.AttachScope(detached);
detached.Complete();
detached.Dispose();
// has self-detached, and is gone!
Assert.AreSame(scope, scopeProvider.AmbientScope);
Assert.AreSame(context, scopeProvider.AmbientContext);
}
2017-05-12 14:49:44 +02:00
Assert.IsNull(scopeProvider.AmbientScope);
Assert.IsNull(scopeProvider.AmbientContext);
}
}
2017-07-20 11:21:28 +02:00
}