2222
2323from ietf .submit .utils import expirable_submissions , expire_submission
2424from ietf .doc .factories import DocumentFactory , WgDraftFactory , IndividualDraftFactory
25- from ietf .doc .models import Document , DocAlias , DocEvent , State , BallotPositionDocEvent , DocumentAuthor
25+ from ietf .doc .models import ( Document , DocAlias , DocEvent , State ,
26+ BallotPositionDocEvent , DocumentAuthor , SubmissionDocEvent )
2627from ietf .doc .utils import create_ballot_if_not_open
2728from ietf .group .factories import GroupFactory , RoleFactory
2829from ietf .group .models import Group
@@ -76,6 +77,40 @@ def submission_file(name, rev, group, format, templatename, author=None, email=N
7677 file .name = "%s-%s.%s" % (name , rev , format )
7778 return file , author
7879
80+ def create_draft_submission_with_rev_mismatch (rev = '01' ):
81+ """Create a draft and submission with mismatched version
82+
83+ Creates a rev '00' draft and Submission / SubmissionDocEvent in the 'posted'
84+ state with the requested rev.
85+ """
86+ draft_name = 'draft-authorname-testing-tests'
87+ author = PersonFactory ()
88+
89+ # draft with rev 00
90+ draft = IndividualDraftFactory (
91+ name = draft_name ,
92+ authors = [author ],
93+ rev = '00' ,
94+ )
95+
96+ # submission with rev mismatched to the draft
97+ sub = Submission .objects .create (
98+ name = draft_name ,
99+ group = None ,
100+ submission_date = datetime .date .today () - datetime .timedelta (days = 1 ),
101+ rev = rev ,
102+ state_id = 'posted' ,
103+ )
104+ SubmissionDocEvent .objects .create (
105+ doc = draft ,
106+ submission = sub ,
107+ by = author ,
108+ desc = 'Existing SubmissionDocEvent with mismatched revision' ,
109+ rev = sub .rev ,
110+ )
111+ return draft , sub
112+
113+
79114class SubmitTests (TestCase ):
80115 def setUp (self ):
81116 self .saved_idsubmit_staging_path = settings .IDSUBMIT_STAGING_PATH
@@ -128,6 +163,26 @@ def tearDown(self):
128163 settings .SUBMIT_YANG_CATALOG_MODEL_DIR = self .saved_yang_catalog_model_dir
129164
130165
166+ def create_and_post_submission (self , name , rev , author , group = None , formats = ("txt" ,)):
167+ """Helper to create and post a submission"""
168+ url = urlreverse ('ietf.submit.views.upload_submission' )
169+ files = dict ()
170+ for format in formats :
171+ files [format ], __ = submission_file (name , rev , group , format , "test_submission.%s" % format , author = author )
172+
173+ r = self .client .post (url , files )
174+ if r .status_code != 302 :
175+ q = PyQuery (r .content )
176+ print (q ('div.has-error div.alert' ).text ())
177+
178+ self .assertNoFormPostErrors (r , ".has-error,.alert-danger" )
179+
180+ for format in formats :
181+ self .assertTrue (os .path .exists (os .path .join (self .staging_dir , "%s-%s.%s" % (name , rev , format ))))
182+ if format == 'xml' :
183+ self .assertTrue (os .path .exists (os .path .join (self .staging_dir , "%s-%s.%s" % (name , rev , 'html' ))))
184+ return r
185+
131186 def do_submission (self , name , rev , group = None , formats = ["txt" ,], author = None ):
132187 # break early in case of missing configuration
133188 self .assertTrue (os .path .exists (settings .IDSUBMIT_IDNITS_BINARY ))
@@ -141,24 +196,11 @@ def do_submission(self, name, rev, group=None, formats=["txt",], author=None):
141196 self .assertEqual (len (q ('input[type=file][name=xml]' )), 1 )
142197
143198 # submit
144- files = {}
145199 if author is None :
146200 author = PersonFactory ()
147- for format in formats :
148- files [format ], __ = submission_file (name , rev , group , format , "test_submission.%s" % format , author = author )
149-
150- r = self .client .post (url , files )
151- if r .status_code != 302 :
152- q = PyQuery (r .content )
153- print (q ('div.has-error div.alert' ).text ())
154-
155- self .assertNoFormPostErrors (r , ".has-error,.alert-danger" )
156-
201+ r = self .create_and_post_submission (name , rev , author , group , formats )
157202 status_url = r ["Location" ]
158- for format in formats :
159- self .assertTrue (os .path .exists (os .path .join (self .staging_dir , "%s-%s.%s" % (name , rev , format ))))
160- if format == 'xml' :
161- self .assertTrue (os .path .exists (os .path .join (self .staging_dir , "%s-%s.%s" % (name , rev , 'html' ))))
203+
162204 self .assertEqual (Submission .objects .filter (name = name ).count (), 1 )
163205 submission = Submission .objects .get (name = name )
164206 if len (submission .authors ) != 1 :
@@ -1256,6 +1298,39 @@ def test_submit_invalid_yang(self):
12561298 if settings .SUBMIT_YANGLINT_COMMAND and os .path .exists (settings .YANGLINT_BINARY ):
12571299 self .assertIn ("No validation errors" , m )
12581300
1301+ def submit_conflicting_submissiondocevent_rev (self , new_rev = '01' , existing_rev = '01' ):
1302+ """Test submitting a rev when an equal or later SubmissionDocEvent rev exists
1303+
1304+ The situation tested here "should" never come up. However, it may occur due to data
1305+ corruption or other unexpected situations.
1306+ """
1307+ draft , existing_sub = create_draft_submission_with_rev_mismatch (existing_rev )
1308+ mailbox_before = len (outbox )
1309+
1310+ # Submit a "real" rev
1311+ self .create_and_post_submission (draft .name , new_rev , PersonFactory ())
1312+
1313+ # Submission should have gone into the manual state
1314+ self .assertEqual (Submission .objects .filter (name = draft .name ).count (), 2 )
1315+ sub = Submission .objects .exclude (pk = existing_sub .pk ).get (name = draft .name , rev = new_rev )
1316+ self .assertIsNotNone (sub )
1317+ self .assertEqual (sub .state_id , 'manual' )
1318+
1319+ # Ensure that an email notification was sent
1320+ self .assertEqual (len (outbox ), mailbox_before + 1 )
1321+ self .assertTrue ("Manual Post Requested" in outbox [- 1 ]["Subject" ])
1322+ self .assertTrue (draft .name in outbox [- 1 ]["Subject" ])
1323+ expected_error = "Rev %s conflicts with existing submission (%s)" % (new_rev , existing_rev )
1324+ self .assertTrue (expected_error in get_payload_text (outbox [- 1 ]))
1325+
1326+ def test_submit_update_existing_submissiondocevent_rev (self ):
1327+ """An existing SubmissionDocEvent with same rev should trigger manual processing"""
1328+ self .submit_conflicting_submissiondocevent_rev ('01' , '01' )
1329+
1330+ def test_submit_update_later_submissiondocevent_rev (self ):
1331+ """An existing SubmissionDocEvent with later rev should trigger manual processing"""
1332+ self .submit_conflicting_submissiondocevent_rev ('01' , '02' )
1333+
12591334
12601335class ApprovalsTestCase (TestCase ):
12611336 def test_approvals (self ):
@@ -1877,6 +1952,18 @@ def test_api_submit_wrong_revision(self):
18771952 expected = "Invalid revision (revision 00 is expected)"
18781953 self .assertContains (r , expected , status_code = 400 )
18791954
1955+ def test_api_submit_update_existing_submissiondocevent_rev (self ):
1956+ draft , _ = create_draft_submission_with_rev_mismatch (rev = '01' )
1957+ r , _ , __ = self .do_post_submission (rev = '01' , name = draft .name )
1958+ expected = "Submission failed"
1959+ self .assertContains (r , expected , status_code = 409 )
1960+
1961+ def test_api_submit_update_later_submissiondocevent_rev (self ):
1962+ draft , _ = create_draft_submission_with_rev_mismatch (rev = '02' )
1963+ r , _ , __ = self .do_post_submission (rev = '01' , name = draft .name )
1964+ expected = "Submission failed"
1965+ self .assertContains (r , expected , status_code = 409 )
1966+
18801967 def test_api_submit_pending_submission (self ):
18811968 r , author , name = self .do_post_submission ('00' )
18821969 expected = "Upload of"
0 commit comments