|
26 | 26 | * exception statement from your version. |
27 | 27 | */ |
28 | 28 |
|
| 29 | +#include <QDir> |
| 30 | +#include <QFile> |
29 | 31 | #include <QObject> |
| 32 | +#include <QTemporaryDir> |
30 | 33 | #include <QTest> |
31 | 34 |
|
32 | 35 | #include "base/bittorrent/toplevelpayload.h" |
@@ -150,6 +153,127 @@ private slots: |
150 | 153 | const PathList out = applyUniqueSubfolderLayout(partial, sampleId, u"Show"_s); |
151 | 154 | QCOMPARE(out, partial); |
152 | 155 | } |
| 156 | + |
| 157 | + void testPlanDestAbsent() const |
| 158 | + { |
| 159 | + QTemporaryDir tmp; |
| 160 | + QVERIFY(tmp.isValid()); |
| 161 | + const Path storageRoot {tmp.path()}; |
| 162 | + |
| 163 | + const PathList current {Path(u"Show/ep.mkv"_s)}; |
| 164 | + const UniqueSubfolderMigrationPlan plan = makeUniqueSubfolderMigrationPlan( |
| 165 | + current, sampleId, u"Show"_s, storageRoot); |
| 166 | + |
| 167 | + QVERIFY(!plan.blocked); |
| 168 | + QVERIFY(!plan.needsConfirmation()); |
| 169 | + QCOMPARE(plan.renames.size(), 1); |
| 170 | + QCOMPARE(plan.renames.at(0).to, Path(u"Show a19f83c275d1/ep.mkv"_s)); |
| 171 | + } |
| 172 | + |
| 173 | + void testPlanDestExistsNeedsConfirmation() const |
| 174 | + { |
| 175 | + QTemporaryDir tmp; |
| 176 | + QVERIFY(tmp.isValid()); |
| 177 | + const Path storageRoot {tmp.path()}; |
| 178 | + const Path dest = storageRoot / Path(u"Show a19f83c275d1"_s); |
| 179 | + QVERIFY(Utils::Fs::mkpath(dest)); |
| 180 | + |
| 181 | + const PathList current {Path(u"Show/ep.mkv"_s)}; |
| 182 | + const UniqueSubfolderMigrationPlan plan = makeUniqueSubfolderMigrationPlan( |
| 183 | + current, sampleId, u"Show"_s, storageRoot); |
| 184 | + |
| 185 | + QVERIFY(!plan.blocked); |
| 186 | + QVERIFY(plan.needsConfirmation()); |
| 187 | + QCOMPARE(plan.existingUniqueFolder, dest); |
| 188 | + QCOMPARE(plan.renames.size(), 1); |
| 189 | + QCOMPARE(plan.renames.at(0).to, Path(u"Show a19f83c275d1/ep.mkv"_s)); |
| 190 | + } |
| 191 | + |
| 192 | + void testPlanConflictingTorrentPathsStillRenamed() const |
| 193 | + { |
| 194 | + QTemporaryDir tmp; |
| 195 | + QVERIFY(tmp.isValid()); |
| 196 | + const Path storageRoot {tmp.path()}; |
| 197 | + const Path dest = storageRoot / Path(u"Show a19f83c275d1"_s); |
| 198 | + QVERIFY(Utils::Fs::mkpath(dest)); |
| 199 | + // Existing file that conflicts with a torrent path — plan still renames onto it (overwrite). |
| 200 | + QFile conflict {(dest / Path(u"ep.mkv"_s)).data()}; |
| 201 | + QVERIFY(conflict.open(QIODevice::WriteOnly)); |
| 202 | + conflict.write("old"); |
| 203 | + conflict.close(); |
| 204 | + |
| 205 | + const PathList current {Path(u"Show/ep.mkv"_s), Path(u"Show/extra.mkv"_s)}; |
| 206 | + const UniqueSubfolderMigrationPlan plan = makeUniqueSubfolderMigrationPlan( |
| 207 | + current, sampleId, u"Show"_s, storageRoot); |
| 208 | + |
| 209 | + QVERIFY(plan.needsConfirmation()); |
| 210 | + QCOMPARE(plan.renames.size(), 2); |
| 211 | + QCOMPARE(plan.renames.at(0).to, Path(u"Show a19f83c275d1/ep.mkv"_s)); |
| 212 | + QCOMPARE(plan.renames.at(1).to, Path(u"Show a19f83c275d1/extra.mkv"_s)); |
| 213 | + } |
| 214 | + |
| 215 | + void testPlanUnrelatedDestFilesNotInRenameList() const |
| 216 | + { |
| 217 | + QTemporaryDir tmp; |
| 218 | + QVERIFY(tmp.isValid()); |
| 219 | + const Path storageRoot {tmp.path()}; |
| 220 | + const Path dest = storageRoot / Path(u"Show a19f83c275d1"_s); |
| 221 | + QVERIFY(Utils::Fs::mkpath(dest)); |
| 222 | + QFile unrelated {(dest / Path(u"notes.txt"_s)).data()}; |
| 223 | + QVERIFY(unrelated.open(QIODevice::WriteOnly)); |
| 224 | + unrelated.write("keep me"); |
| 225 | + unrelated.close(); |
| 226 | + |
| 227 | + const PathList current {Path(u"Show/ep.mkv"_s)}; |
| 228 | + const UniqueSubfolderMigrationPlan plan = makeUniqueSubfolderMigrationPlan( |
| 229 | + current, sampleId, u"Show"_s, storageRoot); |
| 230 | + |
| 231 | + QVERIFY(plan.needsConfirmation()); |
| 232 | + // Only torrent file paths are planned — unrelated notes.txt is never a rename target. |
| 233 | + QCOMPARE(plan.renames.size(), 1); |
| 234 | + QCOMPARE(plan.renames.at(0).to, Path(u"Show a19f83c275d1/ep.mkv"_s)); |
| 235 | + QVERIFY(QFile::exists((dest / Path(u"notes.txt"_s)).data())); |
| 236 | + } |
| 237 | + |
| 238 | + void testPlanEmptyWhenAlreadyUnique() const |
| 239 | + { |
| 240 | + QTemporaryDir tmp; |
| 241 | + QVERIFY(tmp.isValid()); |
| 242 | + const Path storageRoot {tmp.path()}; |
| 243 | + |
| 244 | + const PathList current {Path(u"Show a19f83c275d1/ep.mkv"_s)}; |
| 245 | + const UniqueSubfolderMigrationPlan plan = makeUniqueSubfolderMigrationPlan( |
| 246 | + current, sampleId, u"Show"_s, storageRoot); |
| 247 | + |
| 248 | + QVERIFY(plan.isEmpty()); |
| 249 | + QVERIFY(!plan.needsConfirmation()); |
| 250 | + } |
| 251 | + |
| 252 | + void testPlanBlockedWhenStorageUnknown() const |
| 253 | + { |
| 254 | + const PathList current {Path(u"Show/ep.mkv"_s)}; |
| 255 | + const UniqueSubfolderMigrationPlan plan = makeUniqueSubfolderMigrationPlan( |
| 256 | + current, sampleId, u"Show"_s, {}); |
| 257 | + |
| 258 | + QVERIFY(plan.blocked); |
| 259 | + QVERIFY(!plan.blockReason.isEmpty()); |
| 260 | + QVERIFY(plan.renames.isEmpty()); |
| 261 | + } |
| 262 | + |
| 263 | + // Layout flag is only set by finishUniqueSubfolderMigration when all renames succeed |
| 264 | + // (TorrentImpl). Plan itself never changes content layout — it only lists renames. |
| 265 | + void testPlanDoesNotImplyLayoutChange() const |
| 266 | + { |
| 267 | + QTemporaryDir tmp; |
| 268 | + QVERIFY(tmp.isValid()); |
| 269 | + const PathList current {Path(u"Show/ep.mkv"_s)}; |
| 270 | + const UniqueSubfolderMigrationPlan plan = makeUniqueSubfolderMigrationPlan( |
| 271 | + current, sampleId, u"Show"_s, Path(tmp.path())); |
| 272 | + |
| 273 | + QVERIFY(!plan.renames.isEmpty()); |
| 274 | + // No layout field on the plan — conversion is deferred until renames complete. |
| 275 | + QVERIFY(!plan.blocked); |
| 276 | + } |
153 | 277 | }; |
154 | 278 |
|
155 | 279 | QTEST_APPLESS_MAIN(TestTopLevelPayload) |
|
0 commit comments