A Tests/NosSmooth.Core.Tests/Commands/Control/ControlCommandsTests.cs => Tests/NosSmooth.Core.Tests/Commands/Control/ControlCommandsTests.cs +14 -0
  
@@ 0,0 1,14 @@
+//
+//  ControlCommandsTests.cs
+//
+//  Copyright (c) František Boháček. All rights reserved.
+//  Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace NosSmooth.Core.Tests.Commands.Control;
+
+/// <summary>
+/// Tests control commands logic.
+/// </summary>
+public class ControlCommandsTests
+{
+}<
\ No newline at end of file
 
A Tests/NosSmooth.Core.Tests/Commands/Control/TakeControlCommandHandlerTests.cs => Tests/NosSmooth.Core.Tests/Commands/Control/TakeControlCommandHandlerTests.cs +14 -0
  
@@ 0,0 1,14 @@
+//
+//  TakeControlCommandHandlerTests.cs
+//
+//  Copyright (c) František Boháček. All rights reserved.
+//  Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace NosSmooth.Core.Tests.Commands.Control;
+
+/// <summary>
+/// Tests handling of take control command.
+/// </summary>
+public class TakeControlCommandHandlerTests
+{
+}<
\ No newline at end of file
 
A Tests/NosSmooth.Core.Tests/Fakes/Packets/Events/PacketEvent.cs => Tests/NosSmooth.Core.Tests/Fakes/Packets/Events/PacketEvent.cs +69 -0
  
@@ 0,0 1,69 @@
+//
+//  PacketEvent.cs
+//
+//  Copyright (c) František Boháček. All rights reserved.
+//  Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using NosSmooth.Core.Client;
+using NosSmooth.Core.Packets;
+using NosSmooth.Packets;
+using NosSmooth.PacketSerializer.Abstractions.Attributes;
+using Remora.Results;
+
+namespace NosSmooth.Core.Tests.Fakes.Packets.Events;
+
+/// <summary>
+/// A fake packet event.
+/// </summary>
+public class PacketEvent : IPreExecutionEvent, IPostExecutionEvent
+{
+    private readonly Func<INostaleClient, PacketSource, IPacket, string, Result> _preHandler;
+    private readonly Func<INostaleClient, PacketSource, IPacket, string, IReadOnlyList<Result>, Result> _postHandler;
+
+    /// <summary>
+    /// Initializes a new instance of the <see cref="PacketEvent"/> class.
+    /// </summary>
+    /// <param name="preHandler">The pre handler.</param>
+    /// <param name="postHandler">The post handler.</param>
+    public PacketEvent
+    (
+        Func<INostaleClient, PacketSource, IPacket, string, Result> preHandler,
+        Func<INostaleClient, PacketSource, IPacket, string, IReadOnlyList<Result>, Result> postHandler
+    )
+    {
+        _preHandler = preHandler;
+        _postHandler = postHandler;
+
+    }
+
+    /// <inheritdoc />
+    public Task<Result> ExecuteBeforeExecutionAsync<TPacket>
+        (INostaleClient client, PacketEventArgs<TPacket> packetArgs, CancellationToken ct = default)
+        where TPacket : IPacket
+        => Task.FromResult(_preHandler(client, packetArgs.Source, packetArgs.Packet, packetArgs.PacketString));
+
+    /// <inheritdoc />
+    public Task<Result> ExecuteAfterExecutionAsync<TPacket>
+    (
+        INostaleClient client,
+        PacketEventArgs<TPacket> packetArgs,
+        IReadOnlyList<Result> executionResults,
+        CancellationToken ct = default
+    )
+        where TPacket : IPacket
+        => Task.FromResult
+        (
+            _postHandler
+            (
+                client,
+                packetArgs.Source,
+                packetArgs.Packet,
+                packetArgs.PacketString,
+                executionResults
+            )
+        );
+}<
\ No newline at end of file
 
A Tests/NosSmooth.Core.Tests/Packets/PacketHandlerTests.Events.cs => Tests/NosSmooth.Core.Tests/Packets/PacketHandlerTests.Events.cs +73 -0
  
@@ 0,0 1,73 @@
+//
+//  PacketHandlerTests.Events.cs
+//
+//  Copyright (c) František Boháček. All rights reserved.
+//  Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Threading.Tasks;
+using Microsoft.Extensions.DependencyInjection;
+using NosSmooth.Core.Commands;
+using NosSmooth.Core.Packets;
+using NosSmooth.Core.Tests.Fakes;
+using NosSmooth.Core.Tests.Fakes.Commands;
+using NosSmooth.Core.Tests.Fakes.Packets;
+using NosSmooth.Core.Tests.Fakes.Packets.Events;
+using NosSmooth.PacketSerializer.Abstractions.Attributes;
+using Remora.Results;
+using Xunit;
+
+namespace NosSmooth.Core.Tests.Packets;
+
+/// <summary>
+/// Tests <see cref="PacketHandler"/>.
+/// </summary>
+public class PacketHandlerTestsEvents
+{
+    /// <summary>
+    /// Tests that the handle packet will call the pre event.
+    /// </summary>
+    /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
+    [Fact]
+    public async Task HandlePacket_CallsPreEvent()
+    {
+        var called = false;
+        var client = new FakeEmptyNostaleClient();
+        var provider = new ServiceCollection()
+            .AddSingleton<PacketHandler>()
+            .AddScoped<IPreExecutionEvent>
+            (
+                _ => new PacketEvent
+                (
+                    (_, _, p, _) =>
+                    {
+                        return Result.FromSuccess();
+                    },
+                    (_, _, _, _, _) => throw new NotImplementedException()
+                )
+            )
+            .AddScoped<IPacketResponder<FakePacket>>
+            (
+                _ => new FakePacketResponder<FakePacket>
+                (
+                    args =>
+                    {
+                        called = true;
+                        return Result.FromSuccess();
+                    }
+                )
+            )
+            .BuildServiceProvider();
+
+        var result = await provider.GetRequiredService<PacketHandler>().HandlePacketAsync
+            (client, PacketSource.Client, new FakePacket("a"), "fake a");
+        Assert.True(result.IsSuccess);
+        Assert.True(called);
+    }
+
+    // TODO: calls pre event
+    // TODO: returns pre event error
+
+    // TODO: calls post event
+    // TODO: passes correct result to post event
+}<
\ No newline at end of file
 
A Tests/NosSmooth.Core.Tests/Packets/PacketHandlerTests.Handling.cs => Tests/NosSmooth.Core.Tests/Packets/PacketHandlerTests.Handling.cs +21 -0
  
@@ 0,0 1,21 @@
+//
+//  PacketHandlerTests.Handling.cs
+//
+//  Copyright (c) František Boháček. All rights reserved.
+//  Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using NosSmooth.Core.Packets;
+
+namespace NosSmooth.Core.Tests.Packets;
+
+/// <summary>
+/// Tests <see cref="PacketHandler"/>.
+/// </summary>
+public class PacketHandlerTestsHandling
+{
+    // TODO: check if arguments are correct
+    // TODO: calls responder
+    // TODO: returns responder error
+    // TODO: returns responder and post event error
+    // TODO: does not call responder if pre event failed
+}<
\ No newline at end of file
 
A Tests/NosSmooth.Core.Tests/Packets/PacketHandlerTests.cs => Tests/NosSmooth.Core.Tests/Packets/PacketHandlerTests.cs +15 -0
  
@@ 0,0 1,15 @@
+//
+//  PacketHandlerTests.cs
+//
+//  Copyright (c) František Boháček. All rights reserved.
+//  Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace NosSmooth.Core.Tests.Packets;
+
+/// <summary>
+/// Tests packet handling.
+/// </summary>
+public partial class PacketHandlerTests
+{
+    // TODO: exceptions
+}<
\ No newline at end of file