ormedGroup function

  1. @isTestGroup
void ormedGroup(
  1. String description,
  2. void body(
    1. DataSource dataSource
    ), {
  3. OrmedTestConfig? config,
  4. DataSource? dataSource,
  5. List<Migration>? migrations,
  6. DatabaseRefreshStrategy? refreshStrategy,
  7. String? testOn,
  8. Timeout? timeout,
  9. dynamic skip,
  10. dynamic tags,
  11. Map<String, dynamic>? onPlatform,
  12. int? retry,
})

Runs a test group with database isolation.

Groups related tests that share a common database setup. Every group gets its own unique DataSource (and thus a unique database) for true concurrency.

Use ormedGroup when you have multiple tests that operate on the same schema and you want to avoid the overhead of creating a fresh database for every single test.

ormedGroup('User tests', (db) {
  ormedTest('creates a user', (db) async {
    // ...
  });
});

Pass config from setUpOrmed for explicit manager association:

final config = setUpOrmed(dataSource: harness.dataSource, ...);
ormedGroup('User tests', (db) { ... }, config: config);

Implementation

@isTestGroup
void ormedGroup(
  String description,
  void Function(DataSource dataSource) body, {
  OrmedTestConfig? config,
  DataSource? dataSource, // Optional override
  List<Migration>? migrations, // Optional override
  DatabaseRefreshStrategy? refreshStrategy,
  String? testOn,
  Timeout? timeout,
  dynamic skip,
  dynamic tags,
  Map<String, dynamic>? onPlatform,
  int? retry,
}) {
  group(
    description,
    () {
      // Resolve the manager to use
      late final TestDatabaseManager manager;
      late final OrmedTestConfig? resolvedConfig;

      // Priority for finding manager:
      // 1. Explicit config parameter
      // 2. Explicit dataSource parameter
      // 3. Parent group's context
      // 4. Current Zone's config
      // 5. Last registered config (legacy fallback)
      // 6. Any available manager (last resort)

      if (config != null) {
        // Explicit config provided - use it
        manager = config.manager;
        resolvedConfig = config;
      } else if (dataSource != null) {
        // Custom dataSource provided - look up its manager or find matching one
        resolvedConfig = null;
        final dsName = dataSource.options.name;

        // First, look for a manager registered for this dataSource
        TestDatabaseManager? foundManager;
        for (final entry in _managers.entries) {
          if (entry.value.baseDataSource.options.name == dsName) {
            foundManager = entry.value;
            break;
          }
        }

        if (foundManager == null) {
          throw StateError(
            'No manager found for DataSource "$dsName". '
            'Call setUpOrmed() with this DataSource first.',
          );
        }

        // Create a new manager with overrides if needed
        if (migrations != null || refreshStrategy != null) {
          manager = TestDatabaseManager(
            baseDataSource: dataSource,
            migrations: migrations ?? foundManager.migrations,
            migrationDescriptors: foundManager.migrationDescriptors,
            seeders: foundManager.seeders,
            strategy: refreshStrategy ?? foundManager.strategy,
            adapterFactory: foundManager.adapterFactory,
            runMigrations: foundManager.runMigrations,
          );
        } else {
          manager = foundManager;
        }
      } else if (_currentGroupContext?.config != null) {
        // Use parent group's config
        resolvedConfig = _currentGroupContext!.config;
        manager = resolvedConfig!.manager;
      } else if (_currentGroupContext != null) {
        // Use parent group's manager (no config)
        resolvedConfig = null;
        manager = _currentGroupContext!.manager;
      } else if (_currentZoneConfig != null) {
        // Use Zone-local config
        resolvedConfig = _currentZoneConfig;
        manager = resolvedConfig!.manager;
      } else if (_lastRegisteredConfig != null) {
        // Legacy fallback: use last registered config
        resolvedConfig = _lastRegisteredConfig;
        manager = resolvedConfig!.manager;
      } else if (_managers.isNotEmpty) {
        // Last resort: use any available manager
        resolvedConfig = null;
        manager = _managers.values.first;
        // Log a warning - this shouldn't happen in well-structured tests
        print(
          '[ormed_test] Warning: No explicit config found for ormedGroup "$description". '
          'Using first available manager. Consider passing config explicitly.',
        );
      } else {
        throw StateError(
          'No test manager available. Call setUpOrmed() in main() first, '
          'or pass config parameter to ormedGroup.',
        );
      }

      final strategy = refreshStrategy ?? manager.strategy;
      final groupId = _generateGroupId();

      // Every group gets its own unique DataSource for true concurrency.
      // This ensures that tests in different groups (even in the same file)
      // do not interfere with each other.
      final groupDataSource = manager.createDataSource(groupId);

      final context = _GroupContext(
        manager,
        strategy,
        groupId,
        config: resolvedConfig,
      );
      context.dataSource = groupDataSource;

      runZoned(() {
        if (dataSource != null) {
          setUpAll(() async {
            await manager.initialize();
          });
        }

        // Provision the unique database for the group
        setUpAll(() async {
          await manager.provisionDatabase(groupDataSource);
        });

        tearDownAll(() async {
          await manager.dropDatabase(groupDataSource);
        });

        if (strategy == DatabaseIsolationStrategy.migrateWithTransactions) {
          setUp(() async {
            await groupDataSource.beginTransaction();
            groupDataSource.setAsDefault();
          });

          tearDown(() async {
            await groupDataSource.rollback();
          });
        } else {
          setUp(() async {
            final ds = groupDataSource;
            final schemaManager = manager.getSchemaManager(ds);

            if (schemaManager != null &&
                strategy == DatabaseIsolationStrategy.truncate) {
              // Fast path: truncate tables
              await schemaManager.truncateAll();
            } else if (strategy == DatabaseIsolationStrategy.recreate) {
              // Slow path: full reset
              if (schemaManager != null) {
                await schemaManager.reset();
              } else {
                await manager.migrate(ds);
              }
            }
            ds.setAsDefault();
          });
        }

        // Execute body to define tests
        body(groupDataSource);
      }, zoneValues: {_groupContextKey: context});
    },
    testOn: testOn,
    timeout: timeout,
    skip: skip,
    tags: tags,
    onPlatform: onPlatform,
    retry: retry,
  );
}