Repository: dbt-labs/dbt-core

Issue:
Partial parsing: duplicate resource error (redux)
### Describe the bug
Similar to https://github.com/fishtown-analytics/dbt/issues/2055, the `--partial-parse` flag causes dbt to raise a compilation error for duplicate resources when no such duplicates exist with the following `dbt_project.yml` config:

```yml
...
source-paths: ["models", "tests"]
test-paths: ["tests"]

....
```

I'm a little bit stumped as to why this is happening, but I can replicate it pretty readily.

### Steps To Reproduce

1. Update `dbt_project.yml` to look include:
```yml
source-paths: ["models", "tests"]
test-paths: ["tests"]
```

2. Add a sql file (`select 1 as id`) in the `tests/` directory

3. run the following commands:
```
rm -rf target
dbt --partial-parse compile
dbt --partial-parse compile
```

The second invocation should result in:
```
Running with dbt=0.15.2-a1
Encountered an error:
Compilation Error
  dbt found two resources with the name "my_test". Since these resources have the same name,
  dbt will be unable to find the correct resource when ref("my_test") is used. To fix this,
  change the name of one of these resources:
  - model.my_project.my_test (tests/my_test.sql)
  - model.my_project.my_test (tests/my_test.sql)
```

**The output of `dbt --version`:**
Using 0.15.1-a2 with this PR included: https://github.com/fishtown-analytics/dbt/pull/2056


Discussion:

Resolving patch:
diff --git a/core/dbt/parser/manifest.py b/core/dbt/parser/manifest.py
--- a/core/dbt/parser/manifest.py
+++ b/core/dbt/parser/manifest.py
@@ -129,13 +129,14 @@ def parse_with_cache(
         old_results: Optional[ParseResult],
     ) -> None:
         block = self._get_file(path, parser)
-        if not self._get_cached(block, old_results):
+        if not self._get_cached(block, old_results, parser):
             parser.parse_file(block)
 
     def _get_cached(
         self,
         block: FileBlock,
         old_results: Optional[ParseResult],
+        parser: BaseParser,
     ) -> bool:
         # TODO: handle multiple parsers w/ same files, by
         # tracking parser type vs node type? Or tracking actual
@@ -143,7 +144,9 @@ def _get_cached(
         if old_results is None:
             return False
         if old_results.has_file(block.file):
-            return self.results.sanitized_update(block.file, old_results)
+            return self.results.sanitized_update(
+                block.file, old_results, parser.resource_type
+            )
         return False
 
     def _get_file(self, path: FilePath, parser: BaseParser) -> FileBlock:
diff --git a/core/dbt/parser/results.py b/core/dbt/parser/results.py
--- a/core/dbt/parser/results.py
+++ b/core/dbt/parser/results.py
@@ -14,6 +14,7 @@
     raise_duplicate_resource_name, raise_duplicate_patch_name,
     CompilationException, InternalException
 )
+from dbt.node_types import NodeType
 from dbt.version import __version__
 
 
@@ -149,7 +150,10 @@ def _process_node(
             )
 
     def sanitized_update(
-        self, source_file: SourceFile, old_result: 'ParseResult',
+        self,
+        source_file: SourceFile,
+        old_result: 'ParseResult',
+        resource_type: NodeType,
     ) -> bool:
         """Perform a santized update. If the file can't be updated, invalidate
         it and return false.
@@ -180,6 +184,8 @@ def sanitized_update(
         # the node ID could be in old_result.disabled AND in old_result.nodes.
         # In that case, we have to make sure the path also matches.
         for node_id in old_file.nodes:
+            if old_result.nodes[node_id].resource_type != resource_type:
+                continue
             self._process_node(node_id, source_file, old_file, old_result)
 
         for name in old_file.patches:


Test patch:
diff --git a/test/integration/025_duplicate_model_test/test_duplicate_model.py b/test/integration/025_duplicate_model_test/test_duplicate_model.py
--- a/test/integration/025_duplicate_model_test/test_duplicate_model.py
+++ b/test/integration/025_duplicate_model_test/test_duplicate_model.py
@@ -167,3 +167,25 @@ def test_postgres_duplicate_model_disabled_across_packages(self):
                 .format(schema=self.unique_schema())
         result = self.run_sql(query, fetch="one")[0]
         assert result == 1
+
+
+class TestModelTestOverlap(DBTIntegrationTest):
+
+    @property
+    def schema(self):
+        return "duplicate_model_025"
+
+    @property
+    def models(self):
+        return "models-3"
+
+    @property
+    def project_config(self):
+        return {'test-paths': [self.models]}
+
+    @use_profile('postgres')
+    def test_postgres_duplicate_test_model_paths(self):
+        # this should be ok: test/model overlap is fine
+        self.run_dbt(['compile'])
+        self.run_dbt(['--partial-parse', 'compile'])
+        self.run_dbt(['--partial-parse', 'compile'])