From 961fd13c551b3e50040acb7c914a00ead92de63f Mon Sep 17 00:00:00 2001
From: fluzz <fluzz@freedroid.org>
Date: Thu, 24 Aug 2023 12:03:59 +0200
Subject: [PATCH] Rewrite the 'AutoDate' tests using subtests

Also add a test to check the permissions to set a date, and a test
to check update dates on milestones.

The tests related to 'AutoDate' are:
- TestAPIEditIssueAutoDate
- TestAPIAddIssueLabelsAutoDate
- TestAPIEditIssueMilestoneAutoDate
- TestAPICreateIssueAttachmentAutoDate
- TestAPICreateCommentAutoDate
- TestAPIEditCommentWithDate
- TestAPICreateCommentAttachmentAutoDate
---
 .../api_comment_attachment_test.go            | 106 ++++++------
 tests/integration/api_comment_test.go         | 153 ++++++++---------
 .../integration/api_issue_attachment_test.go  | 119 ++++++-------
 tests/integration/api_issue_label_test.go     |  71 ++++----
 tests/integration/api_issue_test.go           | 160 ++++++++++++++----
 5 files changed, 332 insertions(+), 277 deletions(-)

diff --git a/tests/integration/api_comment_attachment_test.go b/tests/integration/api_comment_attachment_test.go
index 732e7e8a96..9761e06987 100644
--- a/tests/integration/api_comment_attachment_test.go
+++ b/tests/integration/api_comment_attachment_test.go
@@ -112,7 +112,7 @@ func TestAPICreateCommentAttachment(t *testing.T) {
 	unittest.AssertExistsAndLoadBean(t, &repo_model.Attachment{ID: apiAttachment.ID, CommentID: comment.ID})
 }
 
-func TestAPICreateCommentAttachmentWithAutoDate(t *testing.T) {
+func TestAPICreateCommentAttachmentAutoDate(t *testing.T) {
 	defer tests.PrepareTestEnv(t)()
 
 	comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 2})
@@ -129,73 +129,63 @@ func TestAPICreateCommentAttachmentWithAutoDate(t *testing.T) {
 	buff := generateImg()
 	body := &bytes.Buffer{}
 
-	// Setup multi-part
-	writer := multipart.NewWriter(body)
-	part, err := writer.CreateFormFile("attachment", filename)
-	assert.NoError(t, err)
-	_, err = io.Copy(part, &buff)
-	assert.NoError(t, err)
-	err = writer.Close()
-	assert.NoError(t, err)
+	t.Run("WithAutoDate", func(t *testing.T) {
+		defer tests.PrintCurrentTest(t)()
 
-	req := NewRequestWithBody(t, "POST", urlStr, body)
-	req.Header.Add("Content-Type", writer.FormDataContentType())
-	resp := session.MakeRequest(t, req, http.StatusCreated)
+		// Setup multi-part
+		writer := multipart.NewWriter(body)
+		part, err := writer.CreateFormFile("attachment", filename)
+		assert.NoError(t, err)
+		_, err = io.Copy(part, &buff)
+		assert.NoError(t, err)
+		err = writer.Close()
+		assert.NoError(t, err)
 
-	apiAttachment := new(api.Attachment)
-	DecodeJSON(t, resp, &apiAttachment)
+		req := NewRequestWithBody(t, "POST", urlStr, body)
+		req.Header.Add("Content-Type", writer.FormDataContentType())
+		resp := session.MakeRequest(t, req, http.StatusCreated)
+		apiAttachment := new(api.Attachment)
+		DecodeJSON(t, resp, &apiAttachment)
 
-	unittest.AssertExistsAndLoadBean(t, &repo_model.Attachment{ID: apiAttachment.ID})
-	// the execution of the API call supposedly lasted less than one minute
-	updatedSince := time.Since(apiAttachment.Created)
-	assert.LessOrEqual(t, updatedSince, time.Minute)
+		unittest.AssertExistsAndLoadBean(t, &repo_model.Attachment{ID: apiAttachment.ID})
+		// the execution of the API call supposedly lasted less than one minute
+		updatedSince := time.Since(apiAttachment.Created)
+		assert.LessOrEqual(t, updatedSince, time.Minute)
 
-	commentAfter := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: comment.ID})
-	updatedSince = time.Since(commentAfter.UpdatedUnix.AsTime())
-	assert.LessOrEqual(t, updatedSince, time.Minute)
-}
+		commentAfter := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: comment.ID})
+		updatedSince = time.Since(commentAfter.UpdatedUnix.AsTime())
+		assert.LessOrEqual(t, updatedSince, time.Minute)
+	})
 
-func TestAPICreateCommentAttachmentWithNoAutoDate(t *testing.T) {
-	defer tests.PrepareTestEnv(t)()
+	t.Run("WithUpdateDate", func(t *testing.T) {
+		defer tests.PrintCurrentTest(t)()
 
-	comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 2})
-	issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: comment.IssueID})
-	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID})
-	repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
+		updatedAt := time.Now().Add(-time.Hour).Truncate(time.Second)
+		urlStr += fmt.Sprintf("&updated_at=%s", updatedAt.UTC().Format(time.RFC3339))
 
-	session := loginUser(t, repoOwner.Name)
-	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue)
-	updatedAt := time.Now().Add(-time.Hour).Truncate(time.Second)
-	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/comments/%d/assets?token=%s&updated_at=%s",
-		repoOwner.Name, repo.Name, comment.ID, token, updatedAt.UTC().Format(time.RFC3339))
+		// Setup multi-part
+		writer := multipart.NewWriter(body)
+		part, err := writer.CreateFormFile("attachment", filename)
+		assert.NoError(t, err)
+		_, err = io.Copy(part, &buff)
+		assert.NoError(t, err)
+		err = writer.Close()
+		assert.NoError(t, err)
 
-	filename := "image.png"
-	buff := generateImg()
-	body := &bytes.Buffer{}
+		req := NewRequestWithBody(t, "POST", urlStr, body)
+		req.Header.Add("Content-Type", writer.FormDataContentType())
+		resp := session.MakeRequest(t, req, http.StatusCreated)
+		apiAttachment := new(api.Attachment)
+		DecodeJSON(t, resp, &apiAttachment)
 
-	// Setup multi-part
-	writer := multipart.NewWriter(body)
-	part, err := writer.CreateFormFile("attachment", filename)
-	assert.NoError(t, err)
-	_, err = io.Copy(part, &buff)
-	assert.NoError(t, err)
-	err = writer.Close()
-	assert.NoError(t, err)
+		// dates will be converted into the same tz, in order to compare them
+		utcTZ, _ := time.LoadLocation("UTC")
+		unittest.AssertExistsAndLoadBean(t, &repo_model.Attachment{ID: apiAttachment.ID})
+		assert.Equal(t, updatedAt.In(utcTZ), apiAttachment.Created.In(utcTZ))
 
-	req := NewRequestWithBody(t, "POST", urlStr, body)
-	req.Header.Add("Content-Type", writer.FormDataContentType())
-	resp := session.MakeRequest(t, req, http.StatusCreated)
-
-	apiAttachment := new(api.Attachment)
-	DecodeJSON(t, resp, &apiAttachment)
-
-	// dates will be converted into the same tz, in order to compare them
-	utcTZ, _ := time.LoadLocation("UTC")
-	unittest.AssertExistsAndLoadBean(t, &repo_model.Attachment{ID: apiAttachment.ID})
-	assert.Equal(t, updatedAt.In(utcTZ), apiAttachment.Created.In(utcTZ))
-
-	commentAfter := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: comment.ID})
-	assert.Equal(t, updatedAt.In(utcTZ), commentAfter.UpdatedUnix.AsTime().In(utcTZ))
+		commentAfter := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: comment.ID})
+		assert.Equal(t, updatedAt.In(utcTZ), commentAfter.UpdatedUnix.AsTime().In(utcTZ))
+	})
 }
 
 func TestAPIEditCommentAttachment(t *testing.T) {
diff --git a/tests/integration/api_comment_test.go b/tests/integration/api_comment_test.go
index b3c249eee0..339ffdbe0e 100644
--- a/tests/integration/api_comment_test.go
+++ b/tests/integration/api_comment_test.go
@@ -111,61 +111,56 @@ func TestAPICreateComment(t *testing.T) {
 	unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: updatedComment.ID, IssueID: issue.ID, Content: commentBody})
 }
 
-func TestAPICreateCommentWithAutoDate(t *testing.T) {
+func TestAPICreateCommentAutoDate(t *testing.T) {
 	defer tests.PrepareTestEnv(t)()
-	const commentBody = "Comment body"
 
 	issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{})
 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID})
 	repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
-
 	token := getUserToken(t, repoOwner.Name, auth_model.AccessTokenScopeWriteIssue)
+
 	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/comments?token=%s",
 		repoOwner.Name, repo.Name, issue.Index, token)
-	req := NewRequestWithValues(t, "POST", urlStr, map[string]string{
-		"body": commentBody,
-	})
-	resp := MakeRequest(t, req, http.StatusCreated)
-
-	var updatedComment api.Comment
-	DecodeJSON(t, resp, &updatedComment)
-
-	// the execution of the API call supposedly lasted less than one minute
-	updatedSince := time.Since(updatedComment.Updated)
-	assert.LessOrEqual(t, updatedSince, time.Minute)
-
-	commentAfter := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: updatedComment.ID, IssueID: issue.ID, Content: commentBody})
-	updatedSince = time.Since(commentAfter.UpdatedUnix.AsTime())
-	assert.LessOrEqual(t, updatedSince, time.Minute)
-}
-
-func TestAPICreateCommentWithNoAutoDate(t *testing.T) {
-	defer tests.PrepareTestEnv(t)()
 	const commentBody = "Comment body"
-	updatedAt := time.Now().Add(-time.Hour).Truncate(time.Second)
 
-	issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{})
-	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID})
-	repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
+	t.Run("WithAutoDate", func(t *testing.T) {
+		defer tests.PrintCurrentTest(t)()
 
-	token := getUserToken(t, repoOwner.Name, auth_model.AccessTokenScopeWriteIssue)
-	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/comments?token=%s",
-		repoOwner.Name, repo.Name, issue.Index, token)
-	req := NewRequestWithJSON(t, "POST", urlStr, &api.CreateIssueCommentOption{
-		Body:    commentBody,
-		Updated: &updatedAt,
+		req := NewRequestWithValues(t, "POST", urlStr, map[string]string{
+			"body": commentBody,
+		})
+		resp := MakeRequest(t, req, http.StatusCreated)
+		var updatedComment api.Comment
+		DecodeJSON(t, resp, &updatedComment)
+
+		// the execution of the API call supposedly lasted less than one minute
+		updatedSince := time.Since(updatedComment.Updated)
+		assert.LessOrEqual(t, updatedSince, time.Minute)
+
+		commentAfter := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: updatedComment.ID, IssueID: issue.ID, Content: commentBody})
+		updatedSince = time.Since(commentAfter.UpdatedUnix.AsTime())
+		assert.LessOrEqual(t, updatedSince, time.Minute)
 	})
 
-	resp := MakeRequest(t, req, http.StatusCreated)
+	t.Run("WithUpdateDate", func(t *testing.T) {
+		defer tests.PrintCurrentTest(t)()
 
-	var updatedComment api.Comment
-	DecodeJSON(t, resp, &updatedComment)
+		updatedAt := time.Now().Add(-time.Hour).Truncate(time.Second)
 
-	// dates will be converted into the same tz, in order to compare them
-	utcTZ, _ := time.LoadLocation("UTC")
-	assert.Equal(t, updatedAt.In(utcTZ), updatedComment.Updated.In(utcTZ))
-	commentAfter := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: updatedComment.ID, IssueID: issue.ID, Content: commentBody})
-	assert.Equal(t, updatedAt.In(utcTZ), commentAfter.UpdatedUnix.AsTime().In(utcTZ))
+		req := NewRequestWithJSON(t, "POST", urlStr, &api.CreateIssueCommentOption{
+			Body:    commentBody,
+			Updated: &updatedAt,
+		})
+		resp := MakeRequest(t, req, http.StatusCreated)
+		var updatedComment api.Comment
+		DecodeJSON(t, resp, &updatedComment)
+
+		// dates will be converted into the same tz, in order to compare them
+		utcTZ, _ := time.LoadLocation("UTC")
+		assert.Equal(t, updatedAt.In(utcTZ), updatedComment.Updated.In(utcTZ))
+		commentAfter := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: updatedComment.ID, IssueID: issue.ID, Content: commentBody})
+		assert.Equal(t, updatedAt.In(utcTZ), commentAfter.UpdatedUnix.AsTime().In(utcTZ))
+	})
 }
 
 func TestAPIGetComment(t *testing.T) {
@@ -219,64 +214,58 @@ func TestAPIEditComment(t *testing.T) {
 	unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: comment.ID, IssueID: issue.ID, Content: newCommentBody})
 }
 
-func TestAPIEditCommentWithAutoDate(t *testing.T) {
+func TestAPIEditCommentWithDate(t *testing.T) {
 	defer tests.PrepareTestEnv(t)()
-	const newCommentBody = "This is the new comment body"
 
 	comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{},
 		unittest.Cond("type = ?", issues_model.CommentTypeComment))
 	issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: comment.IssueID})
 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID})
 	repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
-
 	token := getUserToken(t, repoOwner.Name, auth_model.AccessTokenScopeWriteIssue)
+
 	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/comments/%d?token=%s",
 		repoOwner.Name, repo.Name, comment.ID, token)
-	req := NewRequestWithValues(t, "PATCH", urlStr, map[string]string{
-		"body": newCommentBody,
-	})
-	resp := MakeRequest(t, req, http.StatusOK)
-
-	var updatedComment api.Comment
-	DecodeJSON(t, resp, &updatedComment)
-
-	// the execution of the API call supposedly lasted less than one minute
-	updatedSince := time.Since(updatedComment.Updated)
-	assert.LessOrEqual(t, updatedSince, time.Minute)
-
-	commentAfter := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: comment.ID, IssueID: issue.ID, Content: newCommentBody})
-	updatedSince = time.Since(commentAfter.UpdatedUnix.AsTime())
-	assert.LessOrEqual(t, updatedSince, time.Minute)
-}
-
-func TestAPIEditCommentWithNoAutoDate(t *testing.T) {
-	defer tests.PrepareTestEnv(t)()
 	const newCommentBody = "This is the new comment body"
-	updatedAt := time.Now().Add(-time.Hour).Truncate(time.Second)
 
-	comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{},
-		unittest.Cond("type = ?", issues_model.CommentTypeComment))
-	issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: comment.IssueID})
-	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID})
-	repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
+	t.Run("WithAutoDate", func(t *testing.T) {
+		defer tests.PrintCurrentTest(t)()
 
-	token := getUserToken(t, repoOwner.Name, auth_model.AccessTokenScopeWriteIssue)
-	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/comments/%d?token=%s",
-		repoOwner.Name, repo.Name, comment.ID, token)
-	req := NewRequestWithJSON(t, "PATCH", urlStr, &api.EditIssueCommentOption{
-		Body:    newCommentBody,
-		Updated: &updatedAt,
+		req := NewRequestWithValues(t, "PATCH", urlStr, map[string]string{
+			"body": newCommentBody,
+		})
+		resp := MakeRequest(t, req, http.StatusOK)
+		var updatedComment api.Comment
+		DecodeJSON(t, resp, &updatedComment)
+
+		// the execution of the API call supposedly lasted less than one minute
+		updatedSince := time.Since(updatedComment.Updated)
+		assert.LessOrEqual(t, updatedSince, time.Minute)
+
+		commentAfter := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: comment.ID, IssueID: issue.ID, Content: newCommentBody})
+		updatedSince = time.Since(commentAfter.UpdatedUnix.AsTime())
+		assert.LessOrEqual(t, updatedSince, time.Minute)
 	})
-	resp := MakeRequest(t, req, http.StatusOK)
 
-	var updatedComment api.Comment
-	DecodeJSON(t, resp, &updatedComment)
+	t.Run("WithUpdateDate", func(t *testing.T) {
+		defer tests.PrintCurrentTest(t)()
 
-	// dates will be converted into the same tz, in order to compare them
-	utcTZ, _ := time.LoadLocation("UTC")
-	assert.Equal(t, updatedAt.In(utcTZ), updatedComment.Updated.In(utcTZ))
-	commentAfter := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: comment.ID, IssueID: issue.ID, Content: newCommentBody})
-	assert.Equal(t, updatedAt.In(utcTZ), commentAfter.UpdatedUnix.AsTime().In(utcTZ))
+		updatedAt := time.Now().Add(-time.Hour).Truncate(time.Second)
+
+		req := NewRequestWithJSON(t, "PATCH", urlStr, &api.EditIssueCommentOption{
+			Body:    newCommentBody,
+			Updated: &updatedAt,
+		})
+		resp := MakeRequest(t, req, http.StatusOK)
+		var updatedComment api.Comment
+		DecodeJSON(t, resp, &updatedComment)
+
+		// dates will be converted into the same tz, in order to compare them
+		utcTZ, _ := time.LoadLocation("UTC")
+		assert.Equal(t, updatedAt.In(utcTZ), updatedComment.Updated.In(utcTZ))
+		commentAfter := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: comment.ID, IssueID: issue.ID, Content: newCommentBody})
+		assert.Equal(t, updatedAt.In(utcTZ), commentAfter.UpdatedUnix.AsTime().In(utcTZ))
+	})
 }
 
 func TestAPIDeleteComment(t *testing.T) {
diff --git a/tests/integration/api_issue_attachment_test.go b/tests/integration/api_issue_attachment_test.go
index ba72df2400..2250646354 100644
--- a/tests/integration/api_issue_attachment_test.go
+++ b/tests/integration/api_issue_attachment_test.go
@@ -101,7 +101,7 @@ func TestAPICreateIssueAttachment(t *testing.T) {
 	unittest.AssertExistsAndLoadBean(t, &repo_model.Attachment{ID: apiAttachment.ID, IssueID: issue.ID})
 }
 
-func TestAPICreateIssueAttachmentWithAutoDate(t *testing.T) {
+func TestAPICreateIssueAttachmentAutoDate(t *testing.T) {
 	defer tests.PrepareTestEnv(t)()
 
 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
@@ -110,80 +110,71 @@ func TestAPICreateIssueAttachmentWithAutoDate(t *testing.T) {
 
 	session := loginUser(t, repoOwner.Name)
 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue)
-
-	filename := "image.png"
-	buff := generateImg()
-	body := &bytes.Buffer{}
-
-	// Setup multi-part
-	writer := multipart.NewWriter(body)
-	part, err := writer.CreateFormFile("attachment", filename)
-	assert.NoError(t, err)
-	_, err = io.Copy(part, &buff)
-	assert.NoError(t, err)
-	err = writer.Close()
-	assert.NoError(t, err)
-
 	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/assets?token=%s",
 		repoOwner.Name, repo.Name, issue.Index, token)
 
-	req := NewRequestWithBody(t, "POST", urlStr, body)
-	req.Header.Add("Content-Type", writer.FormDataContentType())
-	resp := session.MakeRequest(t, req, http.StatusCreated)
-
-	apiAttachment := new(api.Attachment)
-	DecodeJSON(t, resp, &apiAttachment)
-
-	unittest.AssertExistsAndLoadBean(t, &repo_model.Attachment{ID: apiAttachment.ID, IssueID: issue.ID})
-	// the execution of the API call supposedly lasted less than one minute
-	updatedSince := time.Since(apiAttachment.Created)
-	assert.LessOrEqual(t, updatedSince, time.Minute)
-
-	issueAfter := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: issue.Index})
-	updatedSince = time.Since(issueAfter.UpdatedUnix.AsTime())
-	assert.LessOrEqual(t, updatedSince, time.Minute)
-}
-
-func TestAPICreateIssueAttachmentWithNoAutoDate(t *testing.T) {
-	defer tests.PrepareTestEnv(t)()
-
-	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
-	issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{RepoID: repo.ID})
-	repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
-
-	session := loginUser(t, repoOwner.Name)
-	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue)
-
 	filename := "image.png"
 	buff := generateImg()
 	body := &bytes.Buffer{}
 
-	// Setup multi-part
-	writer := multipart.NewWriter(body)
-	part, err := writer.CreateFormFile("attachment", filename)
-	assert.NoError(t, err)
-	_, err = io.Copy(part, &buff)
-	assert.NoError(t, err)
-	err = writer.Close()
-	assert.NoError(t, err)
+	t.Run("WithAutoDate", func(t *testing.T) {
+		defer tests.PrintCurrentTest(t)()
 
-	updatedAt := time.Now().Add(-time.Hour).Truncate(time.Second)
-	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/assets?token=%s&updated_at=%s",
-		repoOwner.Name, repo.Name, issue.Index, token, updatedAt.UTC().Format(time.RFC3339))
+		// Setup multi-part
+		writer := multipart.NewWriter(body)
+		part, err := writer.CreateFormFile("attachment", filename)
+		assert.NoError(t, err)
+		_, err = io.Copy(part, &buff)
+		assert.NoError(t, err)
+		err = writer.Close()
+		assert.NoError(t, err)
 
-	req := NewRequestWithBody(t, "POST", urlStr, body)
-	req.Header.Add("Content-Type", writer.FormDataContentType())
-	resp := session.MakeRequest(t, req, http.StatusCreated)
+		req := NewRequestWithBody(t, "POST", urlStr, body)
+		req.Header.Add("Content-Type", writer.FormDataContentType())
+		resp := session.MakeRequest(t, req, http.StatusCreated)
 
-	apiAttachment := new(api.Attachment)
-	DecodeJSON(t, resp, &apiAttachment)
+		apiAttachment := new(api.Attachment)
+		DecodeJSON(t, resp, &apiAttachment)
 
-	// dates will be converted into the same tz, in order to compare them
-	utcTZ, _ := time.LoadLocation("UTC")
-	unittest.AssertExistsAndLoadBean(t, &repo_model.Attachment{ID: apiAttachment.ID, IssueID: issue.ID})
-	assert.Equal(t, updatedAt.In(utcTZ), apiAttachment.Created.In(utcTZ))
-	issueAfter := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: issue.ID})
-	assert.Equal(t, updatedAt.In(utcTZ), issueAfter.UpdatedUnix.AsTime().In(utcTZ))
+		unittest.AssertExistsAndLoadBean(t, &repo_model.Attachment{ID: apiAttachment.ID, IssueID: issue.ID})
+		// the execution of the API call supposedly lasted less than one minute
+		updatedSince := time.Since(apiAttachment.Created)
+		assert.LessOrEqual(t, updatedSince, time.Minute)
+
+		issueAfter := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: issue.Index})
+		updatedSince = time.Since(issueAfter.UpdatedUnix.AsTime())
+		assert.LessOrEqual(t, updatedSince, time.Minute)
+	})
+
+	t.Run("WithUpdateDate", func(t *testing.T) {
+		defer tests.PrintCurrentTest(t)()
+
+		updatedAt := time.Now().Add(-time.Hour).Truncate(time.Second)
+		urlStr += fmt.Sprintf("&updated_at=%s", updatedAt.UTC().Format(time.RFC3339))
+
+		// Setup multi-part
+		writer := multipart.NewWriter(body)
+		part, err := writer.CreateFormFile("attachment", filename)
+		assert.NoError(t, err)
+		_, err = io.Copy(part, &buff)
+		assert.NoError(t, err)
+		err = writer.Close()
+		assert.NoError(t, err)
+
+		req := NewRequestWithBody(t, "POST", urlStr, body)
+		req.Header.Add("Content-Type", writer.FormDataContentType())
+		resp := session.MakeRequest(t, req, http.StatusCreated)
+
+		apiAttachment := new(api.Attachment)
+		DecodeJSON(t, resp, &apiAttachment)
+
+		// dates will be converted into the same tz, in order to compare them
+		utcTZ, _ := time.LoadLocation("UTC")
+		unittest.AssertExistsAndLoadBean(t, &repo_model.Attachment{ID: apiAttachment.ID, IssueID: issue.ID})
+		assert.Equal(t, updatedAt.In(utcTZ), apiAttachment.Created.In(utcTZ))
+		issueAfter := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: issue.ID})
+		assert.Equal(t, updatedAt.In(utcTZ), issueAfter.UpdatedUnix.AsTime().In(utcTZ))
+	})
 }
 
 func TestAPIEditIssueAttachment(t *testing.T) {
diff --git a/tests/integration/api_issue_label_test.go b/tests/integration/api_issue_label_test.go
index 3c2bed6be0..a29c75727f 100644
--- a/tests/integration/api_issue_label_test.go
+++ b/tests/integration/api_issue_label_test.go
@@ -16,6 +16,7 @@ import (
 	"code.gitea.io/gitea/models/unittest"
 	user_model "code.gitea.io/gitea/models/user"
 	api "code.gitea.io/gitea/modules/structs"
+	"code.gitea.io/gitea/tests"
 
 	"github.com/stretchr/testify/assert"
 )
@@ -112,57 +113,47 @@ func TestAPIAddIssueLabels(t *testing.T) {
 	unittest.AssertExistsAndLoadBean(t, &issues_model.IssueLabel{IssueID: issue.ID, LabelID: 2})
 }
 
-func TestAPIAddIssueLabelsWithAutoDate(t *testing.T) {
-	assert.NoError(t, unittest.LoadFixtures())
+func TestAPIAddIssueLabelsAutoDate(t *testing.T) {
+	defer tests.PrepareTestEnv(t)()
 
-	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
-	issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{RepoID: repo.ID})
-	_ = unittest.AssertExistsAndLoadBean(t, &issues_model.Label{RepoID: repo.ID, ID: 2})
+	issueBefore := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 3})
+	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issueBefore.RepoID})
 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
 
 	session := loginUser(t, owner.Name)
 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue)
-
 	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/labels?token=%s",
-		repo.OwnerName, repo.Name, issue.Index, token)
-	req := NewRequestWithJSON(t, "POST", urlStr, &api.IssueLabelsOption{
-		Labels: []int64{1, 2},
+		owner.Name, repo.Name, issueBefore.Index, token)
+
+	t.Run("WithAutoDate", func(t *testing.T) {
+		defer tests.PrintCurrentTest(t)()
+
+		req := NewRequestWithJSON(t, "POST", urlStr, &api.IssueLabelsOption{
+			Labels: []int64{1},
+		})
+		MakeRequest(t, req, http.StatusOK)
+
+		issueAfter := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: issueBefore.ID})
+		// the execution of the API call supposedly lasted less than one minute
+		updatedSince := time.Since(issueAfter.UpdatedUnix.AsTime())
+		assert.LessOrEqual(t, updatedSince, time.Minute)
 	})
-	resp := MakeRequest(t, req, http.StatusOK)
-	var apiLabels []*api.Label
-	DecodeJSON(t, resp, &apiLabels)
 
-	issueAfter := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: issue.Index})
-	// the execution of the API call supposedly lasted less than one minute
-	updatedSince := time.Since(issueAfter.UpdatedUnix.AsTime())
-	assert.LessOrEqual(t, updatedSince, time.Minute)
-}
+	t.Run("WithUpdatedDate", func(t *testing.T) {
+		defer tests.PrintCurrentTest(t)()
 
-func TestAPIAddIssueLabelsWithNoAutoDate(t *testing.T) {
-	assert.NoError(t, unittest.LoadFixtures())
+		updatedAt := time.Now().Add(-time.Hour).Truncate(time.Second)
+		req := NewRequestWithJSON(t, "POST", urlStr, &api.IssueLabelsOption{
+			Labels:  []int64{2},
+			Updated: &updatedAt,
+		})
+		MakeRequest(t, req, http.StatusOK)
 
-	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
-	issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{RepoID: repo.ID})
-	_ = unittest.AssertExistsAndLoadBean(t, &issues_model.Label{RepoID: repo.ID, ID: 2})
-	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
-
-	session := loginUser(t, owner.Name)
-	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue)
-	updatedAt := time.Now().Add(-time.Hour).Truncate(time.Second)
-
-	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/labels?token=%s",
-		repo.OwnerName, repo.Name, issue.Index, token)
-	req := NewRequestWithJSON(t, "POST", urlStr, &api.IssueLabelsOption{
-		Labels:  []int64{1, 2},
-		Updated: &updatedAt,
+		// dates will be converted into the same tz, in order to compare them
+		utcTZ, _ := time.LoadLocation("UTC")
+		issueAfter := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: issueBefore.ID})
+		assert.Equal(t, updatedAt.In(utcTZ), issueAfter.UpdatedUnix.AsTime().In(utcTZ))
 	})
-	resp := MakeRequest(t, req, http.StatusOK)
-	var apiLabels []*api.Label
-	DecodeJSON(t, resp, &apiLabels)
-	// dates will be converted into the same tz, in order to compare them
-	utcTZ, _ := time.LoadLocation("UTC")
-	issueAfter := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: issue.Index})
-	assert.Equal(t, updatedAt.In(utcTZ), issueAfter.UpdatedUnix.AsTime().In(utcTZ))
 }
 
 func TestAPIReplaceIssueLabels(t *testing.T) {
diff --git a/tests/integration/api_issue_test.go b/tests/integration/api_issue_test.go
index ac26caf006..8e81bb3cb6 100644
--- a/tests/integration/api_issue_test.go
+++ b/tests/integration/api_issue_test.go
@@ -213,61 +213,155 @@ func TestAPIEditIssue(t *testing.T) {
 	assert.Equal(t, title, issueAfter.Title)
 }
 
-func TestAPIEditIssueWithAutoDate(t *testing.T) {
+func TestAPIEditIssueAutoDate(t *testing.T) {
 	defer tests.PrepareTestEnv(t)()
 
-	issueBefore := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 10})
+	issueBefore := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 13})
 	repoBefore := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issueBefore.RepoID})
 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repoBefore.OwnerID})
 	assert.NoError(t, issueBefore.LoadAttributes(db.DefaultContext))
 
-	session := loginUser(t, owner.Name)
-	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue)
+	t.Run("WithAutoDate", func(t *testing.T) {
+		defer tests.PrintCurrentTest(t)()
 
-	body := "new content!"
-	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d?token=%s", owner.Name, repoBefore.Name, issueBefore.Index, token)
-	req := NewRequestWithJSON(t, "PATCH", urlStr, api.EditIssueOption{
-		Body: &body,
+		// User2 is not owner, but can update the 'public' issue with auto date
+		session := loginUser(t, "user2")
+		token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue)
+		urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d?token=%s", owner.Name, repoBefore.Name, issueBefore.Index, token)
+
+		body := "new content!"
+		req := NewRequestWithJSON(t, "PATCH", urlStr, api.EditIssueOption{
+			Body: &body,
+		})
+		resp := MakeRequest(t, req, http.StatusCreated)
+		var apiIssue api.Issue
+		DecodeJSON(t, resp, &apiIssue)
+
+		// the execution of the API call supposedly lasted less than one minute
+		updatedSince := time.Since(apiIssue.Updated)
+		assert.LessOrEqual(t, updatedSince, time.Minute)
+
+		issueAfter := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: issueBefore.ID})
+		updatedSince = time.Since(issueAfter.UpdatedUnix.AsTime())
+		assert.LessOrEqual(t, updatedSince, time.Minute)
 	})
-	resp := MakeRequest(t, req, http.StatusCreated)
-	var apiIssue api.Issue
-	DecodeJSON(t, resp, &apiIssue)
 
-	// the execution of the API call supposedly lasted less than one minute
-	updatedSince := time.Since(apiIssue.Updated)
-	assert.LessOrEqual(t, updatedSince, time.Minute)
+	t.Run("WithUpdateDate", func(t *testing.T) {
+		defer tests.PrintCurrentTest(t)()
 
-	issueAfter := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 10})
-	updatedSince = time.Since(issueAfter.UpdatedUnix.AsTime())
-	assert.LessOrEqual(t, updatedSince, time.Minute)
+		// User1 is admin, and so can update the issue without auto date
+		session := loginUser(t, "user1")
+		token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue)
+		urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d?token=%s", owner.Name, repoBefore.Name, issueBefore.Index, token)
+
+		body := "new content, with updated time"
+		updatedAt := time.Now().Add(-time.Hour).Truncate(time.Second)
+		req := NewRequestWithJSON(t, "PATCH", urlStr, api.EditIssueOption{
+			Body:    &body,
+			Updated: &updatedAt,
+		})
+		resp := MakeRequest(t, req, http.StatusCreated)
+		var apiIssue api.Issue
+		DecodeJSON(t, resp, &apiIssue)
+
+		// dates are converted into the same tz, in order to compare them
+		utcTZ, _ := time.LoadLocation("UTC")
+		assert.Equal(t, updatedAt.In(utcTZ), apiIssue.Updated.In(utcTZ))
+
+		issueAfter := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: issueBefore.ID})
+		assert.Equal(t, updatedAt.In(utcTZ), issueAfter.UpdatedUnix.AsTime().In(utcTZ))
+	})
+
+	t.Run("WithoutPermission", func(t *testing.T) {
+		defer tests.PrintCurrentTest(t)()
+
+		// User2 is not owner nor admin, and so can't update the issue without auto date
+		session := loginUser(t, "user2")
+		token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue)
+		urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d?token=%s", owner.Name, repoBefore.Name, issueBefore.Index, token)
+
+		body := "new content, with updated time"
+		updatedAt := time.Now().Add(-time.Hour).Truncate(time.Second)
+		req := NewRequestWithJSON(t, "PATCH", urlStr, api.EditIssueOption{
+			Body:    &body,
+			Updated: &updatedAt,
+		})
+		resp := MakeRequest(t, req, http.StatusForbidden)
+		var apiError api.APIError
+		DecodeJSON(t, resp, &apiError)
+
+		assert.Equal(t, "user needs to have admin or owner right", apiError.Message)
+	})
 }
 
-func TestAPIEditIssueWithNoAutoDate(t *testing.T) {
+func TestAPIEditIssueMilestoneAutoDate(t *testing.T) {
 	defer tests.PrepareTestEnv(t)()
 
-	issueBefore := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 10})
+	issueBefore := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1})
 	repoBefore := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issueBefore.RepoID})
+
 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repoBefore.OwnerID})
 	assert.NoError(t, issueBefore.LoadAttributes(db.DefaultContext))
 
 	session := loginUser(t, owner.Name)
 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue)
-
-	body := "new content, with updated time"
-	updatedAt := time.Now().Add(-time.Hour).Truncate(time.Second)
 	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d?token=%s", owner.Name, repoBefore.Name, issueBefore.Index, token)
-	req := NewRequestWithJSON(t, "PATCH", urlStr, api.EditIssueOption{
-		Body:    &body,
-		Updated: &updatedAt,
+
+	t.Run("WithAutoDate", func(t *testing.T) {
+		defer tests.PrintCurrentTest(t)()
+
+		milestone := int64(1)
+		req := NewRequestWithJSON(t, "PATCH", urlStr, api.EditIssueOption{
+			Milestone: &milestone,
+		})
+		MakeRequest(t, req, http.StatusCreated)
+
+		// the execution of the API call supposedly lasted less than one minute
+		milestoneAfter := unittest.AssertExistsAndLoadBean(t, &issues_model.Milestone{ID: milestone})
+		updatedSince := time.Since(milestoneAfter.UpdatedUnix.AsTime())
+		assert.LessOrEqual(t, updatedSince, time.Minute)
+	})
+
+	t.Run("WithPostUpdateDate", func(t *testing.T) {
+		defer tests.PrintCurrentTest(t)()
+
+		// Note: the updated_unix field of the test Milestones is set to NULL
+		// Hence, any date is higher than the Milestone's updated date
+		updatedAt := time.Now().Add(-time.Hour).Truncate(time.Second)
+		milestone := int64(2)
+		req := NewRequestWithJSON(t, "PATCH", urlStr, api.EditIssueOption{
+			Milestone: &milestone,
+			Updated:   &updatedAt,
+		})
+		MakeRequest(t, req, http.StatusCreated)
+
+		// the milestone date should be set to 'updatedAt'
+		// dates are converted into the same tz, in order to compare them
+		utcTZ, _ := time.LoadLocation("UTC")
+		milestoneAfter := unittest.AssertExistsAndLoadBean(t, &issues_model.Milestone{ID: milestone})
+		assert.Equal(t, updatedAt.In(utcTZ), milestoneAfter.UpdatedUnix.AsTime().In(utcTZ))
+	})
+
+	t.Run("WithPastUpdateDate", func(t *testing.T) {
+		defer tests.PrintCurrentTest(t)()
+
+		// Note: This Milestone's updated_unix has been set to Now() by the first subtest
+		milestone := int64(1)
+		milestoneBefore := unittest.AssertExistsAndLoadBean(t, &issues_model.Milestone{ID: milestone})
+
+		updatedAt := time.Now().Add(-time.Hour).Truncate(time.Second)
+		req := NewRequestWithJSON(t, "PATCH", urlStr, api.EditIssueOption{
+			Milestone: &milestone,
+			Updated:   &updatedAt,
+		})
+		MakeRequest(t, req, http.StatusCreated)
+
+		// the milestone date should not change
+		// dates are converted into the same tz, in order to compare them
+		utcTZ, _ := time.LoadLocation("UTC")
+		milestoneAfter := unittest.AssertExistsAndLoadBean(t, &issues_model.Milestone{ID: milestone})
+		assert.Equal(t, milestoneAfter.UpdatedUnix.AsTime().In(utcTZ), milestoneBefore.UpdatedUnix.AsTime().In(utcTZ))
 	})
-	resp := MakeRequest(t, req, http.StatusCreated)
-	var apiIssue api.Issue
-	DecodeJSON(t, resp, &apiIssue)
-	// dates will be converted into the same tz, in order to compare them
-	utcTZ, _ := time.LoadLocation("UTC")
-	assert.Equal(t, updatedAt.In(utcTZ), apiIssue.Updated.In(utcTZ))
-	issueAfter := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 10})
-	assert.Equal(t, updatedAt.In(utcTZ), issueAfter.UpdatedUnix.AsTime().In(utcTZ))
 }
 
 func TestAPISearchIssues(t *testing.T) {