import 'package:drift_dev/src/analysis/driver/state.dart'; import 'package:drift_dev/src/analysis/resolver/intermediate_state.dart'; import 'package:drift_dev/src/analysis/results/element.dart'; import 'package:test/test.dart'; import '../test_utils.dart'; void main() { group('drift files', () { test('finds local elements', () async { final backend = await TestBackend.inTest({ 'a|lib/main.drift': ''' CREATE TABLE foo (bar INTEGER); CREATE VIEW my_view AS SELECT whatever FROM unknown_table; ''', }); final uri = Uri.parse('package:a/main.drift'); final state = await backend.driver.findLocalElements(uri); final discovered = state.discovery; DriftElementId id(String name) => DriftElementId(uri, name); expect(state, hasNoErrors); expect( discovered, isA() .having((e) => e.imports, 'imports', isEmpty) .having( (e) => e.locallyDefinedElements, 'locallyDefinedElements', [ isA() .having((t) => t.ownId, 'ownId', id('foo')), isA() .having((v) => v.ownId, 'ownId', id('my_view')), ], ), ); }); test('reports syntax errors', () async { final backend = await TestBackend.inTest({ 'a|lib/main.drift': ''' CREATE TABLE valid_1 (bar INTEGER); CREATE TABLE EXISTS syntax_error (); CREATE TABLE valid_2 (bar INTEGER); ''', }); final state = await backend.discoverLocalElements('package:a/main.drift'); expect(state.errorsDuringDiscovery, [ isDriftError(contains('Expected a table name')), ]); // The syntax error should only affect the single statement expect( state.discovery, isA().having((e) => e.locallyDefinedElements, 'locallyDefinedElements', hasLength(2))); }); test('warns about duplicate elements', () async { final backend = await TestBackend.inTest({ 'a|lib/main.drift': ''' CREATE TABLE a (id INTEGER); CREATE VIEW a AS VALUES(1,2,3); ''', }); final state = await backend.discoverLocalElements('package:a/main.drift'); expect(state.errorsDuringDiscovery, [ isDriftError(contains('already defines an element named `a`')), ]); final result = state.discovery as DiscoveredDriftFile; expect(result.locallyDefinedElements, [isA()]); }); group('imports', () { test('are resolved', () async { final backend = await TestBackend.inTest({ 'a|lib/a.drift': "import 'b.drift';", 'a|lib/b.drift': "CREATE TABLE foo (bar INTEGER);", }); final state = await backend.discoverLocalElements('package:a/a.drift'); expect(state, hasNoErrors); expect( state.discovery, isA().having( (e) => e.importDependencies, 'importDependencies', [(uri: Uri.parse('package:a/b.drift'), transitive: true)], ), ); expect( backend.driver.cache.knownFiles[Uri.parse('package:a/b.drift')], isNull, reason: 'Discovering local elements should not prepare other files', ); }); test('can handle circular imports', () async { final backend = await TestBackend.inTest({ 'a|lib/a.drift': "import 'a.drift'; import 'b.drift';", 'a|lib/b.drift': "import 'a.drift';", }); final state = await backend.discoverLocalElements('package:a/a.drift'); expect(state, hasNoErrors); }); }); }); group('dart files', () { test('fails for part files', () async { final backend = await TestBackend.inTest({ 'a|lib/a.dart': ''' part of 'b.dart'; ''', 'a|lib/b.dart': ''' part 'a.dart'; ''', }); final uri = Uri.parse('package:a/a.dart'); final state = await backend.driver.findLocalElements(uri); expect(state, hasNoErrors); expect(state.discovery, isA()); }); test('finds tables', () async { final backend = await TestBackend.inTest({ 'a|lib/a.dart': ''' import 'package:drift/drift.dart'; class Users extends Table { IntColumn get id => integer()(); } class Groups extends Table { IntColumn get id => integer()(); String get tableName => 'my_custom_table'; } ''', }); final uri = Uri.parse('package:a/a.dart'); final state = await backend.driver.findLocalElements(uri); expect(state, hasNoErrors); expect( state.discovery, isA().having( (e) => e.locallyDefinedElements, 'locallyDefinedElements', [ isA() .having((t) => t.ownId, 'ownId', DriftElementId(uri, 'users')), isA().having((t) => t.ownId, 'ownId', DriftElementId(uri, 'my_custom_table')), ], ), ); }); test('ignores abstract tables', () async { final backend = await TestBackend.inTest({ 'a|lib/a.dart': ''' import 'package:drift/drift.dart'; abstract class Users extends Table { IntColumn get id => integer()(); } abstract class BaseRelationTable extends Table { Column get parentId; Column get childId; @override Set get primaryKey => {parentId, childId}; } ''', }); final uri = Uri.parse('package:a/a.dart'); final state = await backend.driver.findLocalElements(uri); expect(state, hasNoErrors); expect( state.discovery, isA().having( (e) => e.locallyDefinedElements, 'locallyDefinedElements', [ isA() .having((t) => t.ownId, 'ownId', DriftElementId(uri, 'users')), ], ), ); }); test('table name errors', () async { final backend = await TestBackend.inTest({ 'a|lib/expr.dart': ''' import 'package:drift/drift.dart'; class InvalidExpression extends Table { String get tableName => 'foo'.toLowerCase(); } ''', 'a|lib/getter.dart': ''' import 'package:drift/drift.dart'; class InvalidGetter extends Table { String get tableName { return ''; } } ''', }); for (final source in backend.sourceContents.keys) { final state = await backend.driver.findLocalElements(Uri.parse(source)); expect( state.errorsDuringDiscovery, [isDriftError(contains('must directly return a string literal'))], reason: 'Should report error for $source', ); } }); test('warns about duplicate elements', () async { final backend = await TestBackend.inTest({ 'a|lib/a.dart': ''' import 'package:drift/drift.dart'; class A extends Table { IntColumn get id => integer()(); String get tableName => 'tbl'; } class B extends Table { IntColumn get id => integer()(); String get tableName => 'tbl'; } ''', }); final state = await backend.driver.findLocalElements(Uri.parse('package:a/a.dart')); expect(state.errorsDuringDiscovery, [ isDriftError(contains('already defines an element named `tbl`')), ]); final result = state.discovery as DiscoveredDartLibrary; expect(result.locallyDefinedElements, [ isA() .having((e) => e.dartElement.name, 'dartElement.name', 'A') ]); }); }); }