Skip to content

Commit 299e0f6

Browse files
authored
Merge pull request #37 from peppy/beatmap-metadata-watcher
Add helper class to watch for beatmap metadata changes
2 parents e081234 + e9232be commit 299e0f6

File tree

8 files changed

+389
-23
lines changed

8 files changed

+389
-23
lines changed

.config/dotnet-tools.json

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"version": 1,
3+
"isRoot": true,
4+
"tools": {
5+
"jetbrains.resharper.globaltools": {
6+
"version": "2023.3.3",
7+
"commands": [
8+
"jb"
9+
]
10+
},
11+
"nvika": {
12+
"version": "4.0.0",
13+
"commands": [
14+
"nvika"
15+
]
16+
},
17+
"codefilesanity": {
18+
"version": "0.0.36",
19+
"commands": [
20+
"CodeFileSanity"
21+
]
22+
}
23+
}
24+
}

.github/workflows/ci.yml

+48-23
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,61 @@
1-
name: .NET Core
2-
3-
on:
4-
push:
5-
branches: [ master ]
6-
pull_request:
7-
branches: [ master ]
1+
on: [push, pull_request]
2+
name: Continuous Integration
3+
concurrency:
4+
group: ${{ github.workflow }}-${{ github.ref }}
5+
cancel-in-progress: true
86

97
jobs:
10-
unit-tests:
8+
inspect-code:
9+
name: Code Quality
1110
runs-on: ubuntu-latest
11+
steps:
12+
- name: Checkout
13+
uses: actions/checkout@v4
14+
15+
- name: Install .NET 8.0.x
16+
uses: actions/setup-dotnet@v4
17+
with:
18+
dotnet-version: "8.0.x"
1219

13-
services:
14-
redis:
15-
image: redis
16-
ports:
17-
- 6379:6379
18-
options: >-
19-
--health-cmd "redis-cli ping"
20-
--health-interval 10s
21-
--health-timeout 5s
22-
--health-retries 5
20+
- name: Restore Tools
21+
run: dotnet tool restore
2322

23+
- name: Restore Packages
24+
run: dotnet restore
25+
26+
- name: CodeFileSanity
27+
run: |
28+
# TODO: Add ignore filters and GitHub Workflow Command Reporting in CFS. That way we don't have to do this workaround.
29+
# FIXME: Suppress warnings from templates project
30+
exit_code=0
31+
while read -r line; do
32+
if [[ ! -z "$line" ]]; then
33+
echo "::error::$line"
34+
exit_code=1
35+
fi
36+
done <<< $(dotnet codefilesanity)
37+
exit $exit_code
38+
39+
- name: InspectCode
40+
run: dotnet jb inspectcode $(pwd)/osu.Server.QueueProcessor.sln --build --output="inspectcodereport.xml" --caches-home="inspectcode" --verbosity=WARN
41+
42+
- name: NVika
43+
run: dotnet nvika parsereport "${{github.workspace}}/inspectcodereport.xml" --treatwarningsaserrors
44+
45+
test:
46+
name: Test
47+
runs-on: ubuntu-latest
2448
steps:
2549
- name: Checkout
2650
uses: actions/checkout@v4
51+
2752
- name: Install .NET 8.0.x
2853
uses: actions/setup-dotnet@v4
2954
with:
3055
dotnet-version: "8.0.x"
31-
- name: Install dependencies
32-
run: dotnet restore
33-
- name: Build
34-
run: dotnet build --no-restore
56+
57+
- name: Docker compose
58+
run: docker compose up -d
59+
3560
- name: Test
36-
run: dotnet test --no-restore --verbosity normal
61+
run: dotnet test

docker-compose.yml

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
version: '3.9'
2+
3+
x-env: &x-env
4+
DB_CONNECTION_STRING: Server=db;Database=osu;Uid=osuweb;
5+
DB_HOST: db
6+
DB_USERNAME: 'root'
7+
APP_ENV: 'local'
8+
GITHUB_TOKEN: "${GITHUB_TOKEN}"
9+
BROADCAST_DRIVER: redis
10+
CACHE_DRIVER: redis
11+
NOTIFICATION_REDIS_HOST: redis
12+
REDIS_HOST: redis
13+
SESSION_DRIVER: redis
14+
MYSQL_DATABASE: 'osu'
15+
MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
16+
MYSQL_ROOT_HOST: '%'
17+
18+
services:
19+
# just a placeholder service to ensure we wait for migrator to complete successfully.
20+
ready_for_use:
21+
image: hello-world:latest
22+
depends_on:
23+
migrator:
24+
condition: service_completed_successfully
25+
26+
migrator:
27+
image: pppy/osu-web:latest-dev
28+
command: ['artisan', 'db:setup']
29+
depends_on:
30+
db:
31+
condition: service_healthy
32+
redis:
33+
condition: service_healthy
34+
environment:
35+
<<: *x-env
36+
37+
db:
38+
image: mysql/mysql-server:8.0
39+
environment:
40+
<<: *x-env
41+
volumes:
42+
- database:/var/lib/mysql
43+
ports:
44+
- "${MYSQL_EXTERNAL_PORT:-3306}:3306"
45+
command: --default-authentication-plugin=mysql_native_password
46+
healthcheck:
47+
# important to use 127.0.0.1 instead of localhost as mysql starts twice.
48+
# the first time it listens on sockets but isn't actually ready
49+
# see https://github.com/docker-library/mysql/issues/663
50+
test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1"]
51+
interval: 1s
52+
timeout: 60s
53+
start_period: 60s
54+
55+
redis:
56+
image: redis:latest
57+
ports:
58+
- "${REDIS_EXTERNAL_PORT:-6379}:6379"
59+
healthcheck:
60+
test: ["CMD", "redis-cli", "--raw", "incr", "ping"]
61+
interval: 1s
62+
timeout: 60s
63+
start_period: 60s
64+
65+
volumes:
66+
database:

global.json

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"sdk": {
3+
"version": "8.0.100",
4+
"rollForward": "latestFeature",
5+
"allowPrerelease": false
6+
}
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the MIT Licence.
2+
// See the LICENCE file in the repository root for full licence text.
3+
4+
using System;
5+
using System.Threading;
6+
using System.Threading.Tasks;
7+
using Dapper;
8+
using Xunit;
9+
10+
namespace osu.Server.QueueProcessor.Tests
11+
{
12+
public class BeatmapStatusWatcherTests
13+
{
14+
/// <summary>
15+
/// Checking that processing an empty queue works as expected.
16+
/// </summary>
17+
[Fact]
18+
public async Task TestBasic()
19+
{
20+
var cts = new CancellationTokenSource(10000);
21+
22+
TaskCompletionSource<BeatmapUpdates> tcs = new TaskCompletionSource<BeatmapUpdates>();
23+
using var db = await DatabaseAccess.GetConnectionAsync(cts.Token);
24+
25+
// just a safety measure for now to ensure we don't hit production. since i was running on production until now.
26+
// will throw if not on test database.
27+
if (db.QueryFirstOrDefault<int?>("SELECT `count` FROM `osu_counts` WHERE `name` = 'is_production'") != null)
28+
throw new InvalidOperationException("You are trying to do something very silly.");
29+
30+
await db.ExecuteAsync("TRUNCATE TABLE `bss_process_queue`");
31+
32+
using var poller = await BeatmapStatusWatcher.StartPollingAsync(updates => { tcs.SetResult(updates); }, pollMilliseconds: 100);
33+
34+
await db.ExecuteAsync("INSERT INTO `bss_process_queue` (beatmapset_id) VALUES (1)");
35+
36+
var updates = await tcs.Task.WaitAsync(cts.Token);
37+
38+
Assert.Equal(new[] { 1 }, updates.BeatmapSetIDs);
39+
Assert.Equal(1, updates.LastProcessedQueueID);
40+
41+
tcs = new TaskCompletionSource<BeatmapUpdates>();
42+
43+
await db.ExecuteAsync("INSERT INTO `bss_process_queue` (beatmapset_id) VALUES (2), (3)");
44+
45+
updates = await tcs.Task.WaitAsync(cts.Token);
46+
47+
Assert.Equal(new[] { 2, 3 }, updates.BeatmapSetIDs);
48+
Assert.Equal(3, updates.LastProcessedQueueID);
49+
}
50+
51+
/// <summary>
52+
/// Checking that processing an empty queue works as expected.
53+
/// </summary>
54+
[Fact]
55+
public async Task TestLimit()
56+
{
57+
var cts = new CancellationTokenSource(10000);
58+
59+
TaskCompletionSource<BeatmapUpdates> tcs = new TaskCompletionSource<BeatmapUpdates>();
60+
using var db = await DatabaseAccess.GetConnectionAsync(cts.Token);
61+
62+
// just a safety measure for now to ensure we don't hit production. since i was running on production until now.
63+
// will throw if not on test database.
64+
if (db.QueryFirstOrDefault<int?>("SELECT `count` FROM `osu_counts` WHERE `name` = 'is_production'") != null)
65+
throw new InvalidOperationException("You are trying to do something very silly.");
66+
67+
await db.ExecuteAsync("TRUNCATE TABLE `bss_process_queue`");
68+
69+
using var poller = await BeatmapStatusWatcher.StartPollingAsync(updates => { tcs.SetResult(updates); }, limit: 1, pollMilliseconds: 100);
70+
71+
await db.ExecuteAsync("INSERT INTO `bss_process_queue` (beatmapset_id) VALUES (1)");
72+
73+
var updates = await tcs.Task.WaitAsync(cts.Token);
74+
tcs = new TaskCompletionSource<BeatmapUpdates>();
75+
76+
Assert.Equal(new[] { 1 }, updates.BeatmapSetIDs);
77+
Assert.Equal(1, updates.LastProcessedQueueID);
78+
79+
await db.ExecuteAsync("INSERT INTO `bss_process_queue` (beatmapset_id) VALUES (2), (3)");
80+
81+
updates = await tcs.Task.WaitAsync(cts.Token);
82+
tcs = new TaskCompletionSource<BeatmapUpdates>();
83+
84+
Assert.Equal(new[] { 2 }, updates.BeatmapSetIDs);
85+
Assert.Equal(2, updates.LastProcessedQueueID);
86+
87+
updates = await tcs.Task.WaitAsync(cts.Token);
88+
89+
Assert.Equal(new[] { 3 }, updates.BeatmapSetIDs);
90+
Assert.Equal(3, updates.LastProcessedQueueID);
91+
}
92+
}
93+
}

0 commit comments

Comments
 (0)