2
2
import logging
3
3
from io import BytesIO
4
4
5
+ from django .contrib .auth .models import AnonymousUser
5
6
from django .core .files .uploadedfile import SimpleUploadedFile
6
7
from django .db import connection , models
7
8
from django .test import TestCase , override_settings
11
12
from rest_framework .test import APIRequestFactory , APITestCase
12
13
from rich import print
13
14
15
+ from ami .exports .models import DataExport
14
16
from ami .jobs .models import VALID_JOB_TYPES , Job
15
17
from ami .main .models import (
18
+ Classification ,
16
19
Deployment ,
20
+ Detection ,
17
21
Device ,
18
22
Event ,
19
23
Identification ,
24
28
SourceImage ,
25
29
SourceImageCollection ,
26
30
SourceImageUpload ,
31
+ Tag ,
32
+ TaxaList ,
27
33
Taxon ,
28
34
TaxonRank ,
29
35
group_images_into_events ,
30
36
)
31
37
from ami .ml .models .pipeline import Pipeline
38
+ from ami .ml .models .processing_service import ProcessingService
32
39
from ami .ml .models .project_pipeline_config import ProjectPipelineConfig
33
40
from ami .tests .fixtures .main import create_captures , create_occurrences , create_taxa , setup_test_project
34
41
from ami .tests .fixtures .storage import populate_bucket
@@ -1971,7 +1978,7 @@ def setUp(self) -> None:
1971
1978
password = "password123" ,
1972
1979
is_staff = True ,
1973
1980
)
1974
-
1981
+ # Pre-create related test data
1975
1982
# Draft project with owner
1976
1983
self .project = Project .objects .create (
1977
1984
name = "Draft Only Project" ,
@@ -1988,7 +1995,31 @@ def setUp(self) -> None:
1988
1995
self .project .members .add (self .member )
1989
1996
self .detail_url = f"/api/v2/projects/{ self .project .pk } /"
1990
1997
1991
- #
1998
+ Tag .objects .create (name = "Test Tag" , project = self .project )
1999
+ DataExport .objects .create (
2000
+ user = self .owner ,
2001
+ project = self .project ,
2002
+ format = "json" ,
2003
+ filters = {},
2004
+ filters_display = {},
2005
+ file_url = "https://example.com/export.json" ,
2006
+ record_count = 123 ,
2007
+ file_size = 456789 ,
2008
+ )
2009
+ fake_image = SimpleUploadedFile ("test.jpg" , b"fake image content" , content_type = "image/jpeg" )
2010
+ SourceImageUpload .objects .create (image = fake_image , deployment = self .deployment )
2011
+ occurrence = Occurrence .objects .filter (deployment = self .deployment ).first ()
2012
+ Identification .objects .create (occurrence = occurrence )
2013
+ S3StorageSource .objects .create (
2014
+ name = "Test S3 Source" ,
2015
+ bucket = "test-bucket" ,
2016
+ access_key = "fake-access-key" ,
2017
+ secret_key = "fake-secret-key" ,
2018
+ project = self .project ,
2019
+ )
2020
+ taxon = Taxon .objects .create (name = "Draft Taxon" )
2021
+ taxon .projects .add (self .project )
2022
+ self .non_draft_project = Project .objects .filter (draft = False ).first ()
1992
2023
1993
2024
def _auth_get (self , user , url ):
1994
2025
self .client .force_authenticate (user )
@@ -2073,12 +2104,74 @@ def test_deployment_list_draft_project(self):
2073
2104
ids = [d ["id" ] for d in response .data ["results" ]]
2074
2105
assert self .deployment .pk not in ids
2075
2106
2076
- def test_access_after_publishing_project (self ):
2077
- self .project .draft = False
2078
- self .project .save ()
2107
+ def test_visible_for_user_across_all_models (self ):
2108
+ all_users = {
2109
+ "superuser" : self .superuser ,
2110
+ "owner" : self .owner ,
2111
+ "member" : self .member ,
2112
+ "outsider" : self .outsider ,
2113
+ "anonymous" : AnonymousUser (),
2114
+ }
2115
+
2116
+ project_related_models = [
2117
+ Project ,
2118
+ Device ,
2119
+ Site ,
2120
+ Deployment ,
2121
+ Event ,
2122
+ S3StorageSource ,
2123
+ SourceImage ,
2124
+ Occurrence ,
2125
+ Tag ,
2126
+ SourceImageCollection ,
2127
+ Job ,
2128
+ DataExport ,
2129
+ Taxon ,
2130
+ TaxaList ,
2131
+ ProcessingService ,
2132
+ Pipeline ,
2133
+ SourceImageUpload ,
2134
+ Identification ,
2135
+ Classification ,
2136
+ Detection ,
2137
+ ProjectPipelineConfig ,
2138
+ ]
2139
+
2140
+ for model in project_related_models :
2141
+ project_accessor = model .get_project_accessor ()
2142
+ if project_accessor is None :
2143
+ continue # skip models not related to a project
2144
+
2145
+ # Filter only objects from the test draft project
2146
+ try :
2147
+ if model == Project :
2148
+ draft_queryset = model .objects .filter (draft = True )
2149
+ non_draft_queryset = model .objects .filter (draft = False )
2150
+ else :
2151
+ draft_queryset = model .objects .filter (** {f"{ project_accessor } " : self .project })
2152
+ non_draft_queryset = model .objects .filter (** {f"{ project_accessor } " : self .non_draft_project })
2153
+ except Exception as e :
2154
+ raise AssertionError (
2155
+ f"Failed to filter querysets for { model .__name__ } using accessor '{ project_accessor } ': { e } "
2156
+ )
2157
+
2158
+ self .assertTrue (
2159
+ draft_queryset .exists (),
2160
+ f"No instances found for model { model .__name__ } tied to the draft project" ,
2161
+ )
2162
+
2163
+ for role , user in all_users .items ():
2164
+ visible_ids = list (draft_queryset .visible_for_user (user ).values_list ("id" , flat = True ))
2165
+ non_draft_ids = set (non_draft_queryset .values_list ("id" , flat = True ))
2166
+ is_draft_viewer = role in {"superuser" , "owner" , "member" }
2167
+
2168
+ for instance in draft_queryset :
2169
+ msg = f"{ model .__name__ } visible_for_user failed for role={ role } "
2079
2170
2080
- project_url = f"/api/v2/projects/ { self . project . pk } /"
2081
- deployment_url = f"/api/v2/deployments/ { self . deployment . pk } /"
2171
+ is_in_non_draft = instance . id in non_draft_ids
2172
+ should_be_visible = is_draft_viewer or is_in_non_draft
2082
2173
2083
- assert self ._auth_get (self .outsider , project_url ).status_code == 200
2084
- assert self ._auth_get (self .outsider , deployment_url ).status_code == 200
2174
+ if should_be_visible :
2175
+ self .assertIn (instance .id , visible_ids , msg )
2176
+ else :
2177
+ self .assertNotIn (instance .id , visible_ids , msg )
0 commit comments