feat(0.35.0): jalons en écriture depuis la page Projet (issue #26)
CI / test (push) Successful in 2m22s

- Clients : create_milestone / update_milestone (cache
  invalidé) ; GitHub hérite des mêmes endpoints.
- API Kanbante : création, modification (titre/échéance,
  échéance vidée = retirée), fermeture ; CSRF, validation
  de date avant toute écriture, erreurs forge en 502.
- Page Projet : formulaire 'Nouveau jalon' + édition/
  fermeture par jalon (project.js versionné).
- Échéance à midi UTC : à minuit, le fuseau de l'instance
  Gitea faisait reculer la date affichée d'un jour (réel).
- 291 tests verts (5 nouveaux, palier 24) ; cycle réel
  complet sur Projets/kanbante (jalons smoke #37/#38 créés,
  dates vérifiées, fermés).
This commit is contained in:
Spark
2026-10-26 10:53:29 -04:00
parent 8c889878ee
commit a92353f8ef
10 changed files with 421 additions and 10 deletions
+55 -6
View File
@@ -359,15 +359,64 @@ class FakeGitea:
else:
self.starred.discard(f"{owner}/{repo}")
async def get_milestones(self, owner, repo, state="open"):
return [
@property
def _milestones(self):
return self.__dict__.setdefault(
"milestones_store",
{
"id": 1, "title": "Sprint 1",
"due_on": "2026-10-20T00:00:00Z",
"open_issues": 1, "closed_issues": 1,
}
"Projets/demo": [
{
"id": 1, "title": "Sprint 1",
"description": "",
"due_on": "2026-10-20T00:00:00Z",
"state": "open",
"open_issues": 1, "closed_issues": 1,
}
]
},
)
async def get_milestones(self, owner, repo, state="open"):
items = self._milestones.get(f"{owner}/{repo}", [])
return [
dict(m) for m in items
if state == "all" or m.get("state", "open") == state
]
async def create_milestone(
self, owner, repo, title, description="", due_on=None
):
store = self._milestones.setdefault(f"{owner}/{repo}", [])
milestone = {
"id": 100 + len(store),
"title": title,
"description": description,
"due_on": due_on,
"state": "open",
"open_issues": 0, "closed_issues": 0,
}
store.append(milestone)
return dict(milestone)
async def update_milestone(
self, owner, repo, milestone_id, title=None, description=None,
due_on=None, clear_due=False, state=None,
):
for m in self._milestones.get(f"{owner}/{repo}", []):
if m["id"] == milestone_id:
if title is not None:
m["title"] = title
if description is not None:
m["description"] = description
if clear_due:
m["due_on"] = None
elif due_on:
m["due_on"] = due_on
if state is not None:
m["state"] = state
return dict(m)
raise AssertionError("jalon introuvable dans le faux")
async def create_issue(self, owner, repo, title, body=""):
self._next_number += 1
issue = _issue(self._next_number, title)