123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460 |
- import unittest
- from datetime import datetime, timedelta
- from functools import cached_property
- from unittest.mock import patch
- import pytz
- from django.urls import reverse
- from django.utils import timezone
- from sentry.api.endpoints.organization_releases import ReleaseSerializerWithProjects
- from sentry.api.serializers.rest_framework.release import ReleaseHeadCommitSerializer
- from sentry.auth import access
- from sentry.constants import BAD_RELEASE_CHARS, MAX_COMMIT_LENGTH, MAX_VERSION_LENGTH
- from sentry.locks import locks
- from sentry.models import (
- Activity,
- ApiKey,
- ApiToken,
- Commit,
- CommitAuthor,
- Environment,
- Release,
- ReleaseCommit,
- ReleaseHeadCommit,
- ReleaseProject,
- ReleaseProjectEnvironment,
- ReleaseStages,
- Repository,
- )
- from sentry.models.commitfilechange import CommitFileChange
- from sentry.models.orgauthtoken import OrgAuthToken
- from sentry.plugins.providers.dummy.repository import DummyRepositoryProvider
- from sentry.search.events.constants import (
- RELEASE_ALIAS,
- RELEASE_STAGE_ALIAS,
- SEMVER_ALIAS,
- SEMVER_BUILD_ALIAS,
- SEMVER_PACKAGE_ALIAS,
- )
- from sentry.silo import SiloMode
- from sentry.testutils import (
- APITestCase,
- ReleaseCommitPatchTest,
- SetRefsTestCase,
- SnubaTestCase,
- TestCase,
- )
- from sentry.testutils.outbox import outbox_runner
- from sentry.testutils.silo import assume_test_silo_mode, region_silo_test
- from sentry.types.activity import ActivityType
- from sentry.utils.security.orgauthtoken_token import generate_token, hash_token
- @region_silo_test(stable=True)
- class OrganizationReleaseListTest(APITestCase, SnubaTestCase):
- endpoint = "sentry-api-0-organization-releases"
- def assert_expected_versions(self, response, expected):
- assert [item["version"] for item in response.data] == [e.version for e in expected]
- def test_simple(self):
- user = self.create_user(is_staff=False, is_superuser=False)
- org = self.organization
- org2 = self.create_organization()
- org.flags.allow_joinleave = False
- org.save()
- team1 = self.create_team(organization=org)
- team2 = self.create_team(organization=org)
- project1 = self.create_project(teams=[team1], organization=org)
- project2 = self.create_project(teams=[team2], organization=org2)
- project3 = self.create_project(teams=[team1], organization=org)
- self.create_member(teams=[team1], user=user, organization=org)
- self.login_as(user=user)
- release1 = Release.objects.create(
- organization_id=org.id, version="1", date_added=datetime(2013, 8, 13, 3, 8, 24, 880386)
- )
- release1.add_project(project1)
- release2 = Release.objects.create(
- organization_id=org2.id, version="2", date_added=datetime(2013, 8, 14, 3, 8, 24, 880386)
- )
- release2.add_project(project2)
- release3 = Release.objects.create(
- organization_id=org.id,
- version="3",
- date_added=datetime(2013, 8, 12, 3, 8, 24, 880386),
- date_released=datetime(2013, 8, 15, 3, 8, 24, 880386),
- )
- release3.add_project(project3)
- release4 = Release.objects.create(
- organization_id=org.id, version="4", date_added=datetime(2013, 8, 14, 3, 8, 24, 880386)
- )
- release4.add_project(project3)
- response = self.get_success_response(org.slug)
- self.assert_expected_versions(response, [release4, release1, release3])
- def test_release_list_order_by_date_added(self):
- """
- Test that ensures that by relying on the default date sorting, releases
- will only be sorted according to `Release.date_added`, and
- `Release.date_released` should have no effect whatsoever on that order
- """
- user = self.create_user(is_staff=False, is_superuser=False)
- org = self.organization
- org.flags.allow_joinleave = False
- org.save()
- team = self.create_team(organization=org)
- project = self.create_project(teams=[team], organization=org)
- self.create_member(teams=[team], user=user, organization=org)
- self.login_as(user=user)
- release6 = Release.objects.create(
- organization_id=org.id,
- version="6",
- date_added=datetime(2013, 8, 10, 3, 8, 24, 880386),
- date_released=datetime(2013, 8, 20, 3, 8, 24, 880386),
- )
- release6.add_project(project)
- release7 = Release.objects.create(
- organization_id=org.id,
- version="7",
- date_added=datetime(2013, 8, 12, 3, 8, 24, 880386),
- date_released=datetime(2013, 8, 18, 3, 8, 24, 880386),
- )
- release7.add_project(project)
- release8 = Release.objects.create(
- organization_id=org.id,
- version="8",
- date_added=datetime(2013, 8, 14, 3, 8, 24, 880386),
- date_released=datetime(2013, 8, 16, 3, 8, 24, 880386),
- )
- release8.add_project(project)
- response = self.get_success_response(org.slug)
- self.assert_expected_versions(response, [release8, release7, release6])
- def test_release_list_order_by_sessions_empty(self):
- self.login_as(user=self.user)
- release_1 = self.create_release(version="1")
- release_2 = self.create_release(version="2")
- release_3 = self.create_release(version="3")
- release_4 = self.create_release(version="4")
- release_5 = self.create_release(version="5")
- # Make sure ordering works fine when we have no session data at all
- response = self.get_success_response(self.organization.slug, sort="sessions", flatten="1")
- self.assert_expected_versions(
- response, [release_5, release_4, release_3, release_2, release_1]
- )
- def test_release_list_order_by_sessions(self):
- self.login_as(user=self.user)
- release_1 = self.create_release(version="1")
- self.store_session(self.build_session(release=release_1))
- release_2 = self.create_release(version="2")
- release_3 = self.create_release(version="3")
- release_4 = self.create_release(version="4")
- release_5 = self.create_release(version="5")
- self.bulk_store_sessions([self.build_session(release=release_5) for _ in range(2)])
- response = self.get_success_response(self.organization.slug, sort="sessions", flatten="1")
- self.assert_expected_versions(
- response, [release_5, release_1, release_4, release_3, release_2]
- )
- response = self.get_success_response(
- self.organization.slug, sort="sessions", flatten="1", per_page=1
- )
- self.assert_expected_versions(response, [release_5])
- response = self.get_success_response(
- self.organization.slug,
- sort="sessions",
- flatten="1",
- per_page=1,
- cursor=self.get_cursor_headers(response)[1],
- )
- self.assert_expected_versions(response, [release_1])
- response = self.get_success_response(
- self.organization.slug,
- sort="sessions",
- flatten="1",
- per_page=1,
- cursor=self.get_cursor_headers(response)[1],
- )
- self.assert_expected_versions(response, [release_4])
- response = self.get_success_response(
- self.organization.slug,
- sort="sessions",
- flatten="1",
- per_page=1,
- cursor=self.get_cursor_headers(response)[1],
- )
- self.assert_expected_versions(response, [release_3])
- response = self.get_success_response(
- self.organization.slug,
- sort="sessions",
- flatten="1",
- per_page=1,
- cursor=self.get_cursor_headers(response)[1],
- )
- self.assert_expected_versions(response, [release_2])
- response = self.get_success_response(
- self.organization.slug, sort="sessions", flatten="1", per_page=3
- )
- self.assert_expected_versions(response, [release_5, release_1, release_4])
- response = self.get_success_response(
- self.organization.slug,
- sort="sessions",
- flatten="1",
- per_page=3,
- cursor=self.get_cursor_headers(response)[1],
- )
- self.assert_expected_versions(response, [release_3, release_2])
- def test_release_list_order_by_build_number(self):
- self.login_as(user=self.user)
- release_1 = self.create_release(version="test@1.2+1000")
- release_2 = self.create_release(version="test@1.2+1")
- release_3 = self.create_release(version="test@1.2+200")
- self.create_release(version="test@1.2")
- self.create_release(version="test@1.2+500alpha")
- response = self.get_success_response(self.organization.slug, sort="build")
- self.assert_expected_versions(response, [release_1, release_3, release_2])
- def test_release_list_order_by_semver(self):
- self.login_as(user=self.user)
- release_1 = self.create_release(version="test@2.2")
- release_2 = self.create_release(version="test@10.0+122")
- release_3 = self.create_release(version="test@2.2-alpha")
- release_4 = self.create_release(version="test@2.2.3")
- release_5 = self.create_release(version="test@2.20.3")
- release_6 = self.create_release(version="test@2.20.3.3")
- release_7 = self.create_release(version="test@10.0+123")
- release_8 = self.create_release(version="test@some_thing")
- release_9 = self.create_release(version="random_junk")
- response = self.get_success_response(self.organization.slug, sort="semver")
- self.assert_expected_versions(
- response,
- [
- release_7,
- release_2,
- release_6,
- release_5,
- release_4,
- release_1,
- release_3,
- release_9,
- release_8,
- ],
- )
- def test_query_filter(self):
- user = self.create_user(is_staff=False, is_superuser=False)
- org = self.organization
- org.flags.allow_joinleave = False
- org.save()
- team = self.create_team(organization=org)
- project = self.create_project(teams=[team], organization=org)
- self.create_member(teams=[team], user=user, organization=org)
- self.login_as(user=user)
- release = Release.objects.create(
- organization_id=org.id,
- version="foobar",
- date_added=datetime(2013, 8, 13, 3, 8, 24, 880386),
- )
- release.add_project(project)
- release2 = Release.objects.create(
- organization_id=org.id,
- version="sdfsdfsdf",
- date_added=datetime(2013, 8, 13, 3, 8, 24, 880386),
- )
- release2.add_project(project)
- response = self.get_success_response(org.slug, query="oob")
- self.assert_expected_versions(response, [release])
- response = self.get_success_response(org.slug, query="baz")
- self.assert_expected_versions(response, [])
- def test_release_filter(self):
- user = self.create_user(is_staff=False, is_superuser=False)
- org = self.organization
- org.flags.allow_joinleave = False
- org.save()
- team = self.create_team(organization=org)
- project = self.create_project(teams=[team], organization=org)
- self.create_member(teams=[team], user=user, organization=org)
- self.login_as(user=user)
- release = Release.objects.create(
- organization_id=org.id,
- version="foobar",
- date_added=datetime(2013, 8, 13, 3, 8, 24, 880386),
- )
- release.add_project(project)
- release2 = Release.objects.create(
- organization_id=org.id,
- version="sdfsdfsdf",
- date_added=datetime(2013, 8, 13, 3, 8, 24, 880386),
- )
- release2.add_project(project)
- response = self.get_success_response(
- self.organization.slug, query=f"{RELEASE_ALIAS}:foobar"
- )
- self.assert_expected_versions(response, [release])
- response = self.get_success_response(self.organization.slug, query=f"{RELEASE_ALIAS}:foo*")
- self.assert_expected_versions(response, [release])
- response = self.get_success_response(self.organization.slug, query=f"{RELEASE_ALIAS}:baz")
- self.assert_expected_versions(response, [])
- # NOT release
- response = self.get_success_response(
- self.organization.slug, query=f"!{RELEASE_ALIAS}:foobar"
- )
- self.assert_expected_versions(response, [release2])
- def test_query_filter_suffix(self):
- user = self.create_user(is_staff=False, is_superuser=False)
- org = self.organization
- org.flags.allow_joinleave = False
- org.save()
- team = self.create_team(organization=org)
- project = self.create_project(teams=[team], organization=org)
- self.create_member(teams=[team], user=user, organization=org)
- self.login_as(user=user)
- release = Release.objects.create(
- organization_id=org.id,
- version="com.foo.BarApp@1.0+1234",
- date_added=datetime(2013, 8, 13, 3, 8, 24, 880386),
- )
- release.add_project(project)
- url = reverse("sentry-api-0-organization-releases", kwargs={"organization_slug": org.slug})
- response = self.client.get(url + "?query=1.0+(1234)", format="json")
- assert response.status_code == 200, response.content
- assert len(response.data) == 1
- assert response.data[0]["version"] == release.version
- url = reverse("sentry-api-0-organization-releases", kwargs={"organization_slug": org.slug})
- response = self.client.get(url + "?query=1.0%2B1234", format="json")
- assert response.status_code == 200, response.content
- assert len(response.data) == 1
- assert response.data[0]["version"] == release.version
- def test_semver_filter(self):
- self.login_as(user=self.user)
- release_1 = self.create_release(version="test@1.2.4+124")
- release_2 = self.create_release(version="test@1.2.3+123")
- release_3 = self.create_release(version="test2@1.2.5+125")
- release_4 = self.create_release(version="some.release")
- response = self.get_success_response(self.organization.slug, query=f"{SEMVER_ALIAS}:>1.2.3")
- self.assert_expected_versions(response, [release_3, release_1])
- response = self.get_success_response(
- self.organization.slug, query=f"{SEMVER_ALIAS}:>=1.2.3"
- )
- self.assert_expected_versions(response, [release_3, release_2, release_1])
- response = self.get_success_response(self.organization.slug, query=f"{SEMVER_ALIAS}:1.2.*")
- self.assert_expected_versions(response, [release_3, release_2, release_1])
- # NOT semver version
- response = self.get_success_response(self.organization.slug, query=f"!{SEMVER_ALIAS}:1.2.3")
- self.assert_expected_versions(response, [release_4, release_3, release_1])
- response = self.get_success_response(
- self.organization.slug, query=f"{SEMVER_ALIAS}:>=1.2.3", sort="semver"
- )
- self.assert_expected_versions(response, [release_3, release_1, release_2])
- response = self.get_success_response(self.organization.slug, query=f"{SEMVER_ALIAS}:2.2.1")
- self.assert_expected_versions(response, [])
- response = self.get_success_response(
- self.organization.slug, query=f"{SEMVER_PACKAGE_ALIAS}:test2"
- )
- self.assert_expected_versions(response, [release_3])
- response = self.get_success_response(
- self.organization.slug, query=f"{SEMVER_PACKAGE_ALIAS}:test"
- )
- self.assert_expected_versions(response, [release_2, release_1])
- # NOT semver package
- response = self.get_success_response(
- self.organization.slug, query=f"!{SEMVER_PACKAGE_ALIAS}:test2"
- )
- self.assert_expected_versions(response, [release_4, release_2, release_1])
- response = self.get_success_response(
- self.organization.slug, query=f"{SEMVER_BUILD_ALIAS}:>124"
- )
- self.assert_expected_versions(response, [release_3])
- response = self.get_success_response(
- self.organization.slug, query=f"{SEMVER_BUILD_ALIAS}:<125"
- )
- self.assert_expected_versions(response, [release_2, release_1])
- # NOT semver build
- response = self.get_success_response(
- self.organization.slug, query=f"!{SEMVER_BUILD_ALIAS}:125"
- )
- self.assert_expected_versions(response, [release_4, release_2, release_1])
- def test_release_stage_filter(self):
- self.login_as(user=self.user)
- response = self.get_success_response(
- self.organization.slug,
- query=f"{RELEASE_STAGE_ALIAS}:adopted",
- environment=self.environment.name,
- )
- assert [r["version"] for r in response.data] == []
- replaced_release = self.create_release(version="replaced_release")
- adopted_release = self.create_release(version="adopted_release")
- not_adopted_release = self.create_release(version="not_adopted_release")
- adopted_rpe = ReleaseProjectEnvironment.objects.create(
- project_id=self.project.id,
- release_id=adopted_release.id,
- environment_id=self.environment.id,
- adopted=timezone.now(),
- )
- ReleaseProjectEnvironment.objects.create(
- project_id=self.project.id,
- release_id=replaced_release.id,
- environment_id=self.environment.id,
- adopted=timezone.now() - timedelta(minutes=5),
- unadopted=timezone.now(),
- )
- ReleaseProjectEnvironment.objects.create(
- project_id=self.project.id,
- release_id=not_adopted_release.id,
- environment_id=self.environment.id,
- )
- response = self.get_success_response(
- self.organization.slug,
- query=f"{RELEASE_STAGE_ALIAS}:{ReleaseStages.ADOPTED}",
- environment=self.environment.name,
- )
- self.assert_expected_versions(response, [adopted_release])
- response = self.get_success_response(
- self.organization.slug,
- query=f"{RELEASE_STAGE_ALIAS}:{ReleaseStages.LOW_ADOPTION}",
- environment=self.environment.name,
- )
- self.assert_expected_versions(response, [not_adopted_release])
- response = self.get_success_response(
- self.organization.slug,
- query=f"{RELEASE_STAGE_ALIAS}:{ReleaseStages.REPLACED}",
- environment=self.environment.name,
- )
- self.assert_expected_versions(response, [replaced_release])
- # NOT release stage
- response = self.get_success_response(
- self.organization.slug,
- query=f"!{RELEASE_STAGE_ALIAS}:{ReleaseStages.REPLACED}",
- environment=self.environment.name,
- )
- self.assert_expected_versions(response, [not_adopted_release, adopted_release])
- response = self.get_success_response(
- self.organization.slug,
- query=f"{RELEASE_STAGE_ALIAS}:[{ReleaseStages.ADOPTED},{ReleaseStages.REPLACED}]",
- environment=self.environment.name,
- )
- self.assert_expected_versions(response, [adopted_release, replaced_release])
- response = self.get_success_response(
- self.organization.slug,
- query=f"{RELEASE_STAGE_ALIAS}:[{ReleaseStages.LOW_ADOPTION}]",
- environment=self.environment.name,
- )
- self.assert_expected_versions(response, [not_adopted_release])
- response = self.get_success_response(
- self.organization.slug,
- sort="adoption",
- )
- self.assert_expected_versions(
- response, [adopted_release, replaced_release, not_adopted_release]
- )
- adopted_rpe.update(adopted=timezone.now() - timedelta(minutes=15))
- # Replaced should come first now.
- response = self.get_success_response(
- self.organization.slug,
- sort="adoption",
- )
- self.assert_expected_versions(
- response, [replaced_release, adopted_release, not_adopted_release]
- )
- response = self.get_success_response(self.organization.slug, sort="adoption", per_page=1)
- self.assert_expected_versions(response, [replaced_release])
- next_cursor = self.get_cursor_headers(response)[1]
- response = self.get_success_response(
- self.organization.slug,
- sort="adoption",
- per_page=1,
- cursor=next_cursor,
- )
- self.assert_expected_versions(response, [adopted_release])
- next_cursor = self.get_cursor_headers(response)[1]
- response = self.get_success_response(
- self.organization.slug,
- sort="adoption",
- per_page=1,
- cursor=next_cursor,
- )
- prev_cursor = self.get_cursor_headers(response)[0]
- self.assert_expected_versions(response, [not_adopted_release])
- response = self.get_success_response(
- self.organization.slug,
- sort="adoption",
- per_page=1,
- cursor=prev_cursor,
- )
- prev_cursor = self.get_cursor_headers(response)[0]
- self.assert_expected_versions(response, [adopted_release])
- response = self.get_success_response(
- self.organization.slug,
- sort="adoption",
- per_page=1,
- cursor=prev_cursor,
- )
- prev_cursor = self.get_cursor_headers(response)[0]
- self.assert_expected_versions(response, [replaced_release])
- adopted_rpe.update(adopted=timezone.now() - timedelta(minutes=15))
- response = self.get_success_response(
- self.organization.slug,
- query=f"{RELEASE_STAGE_ALIAS}:[{ReleaseStages.LOW_ADOPTION},{ReleaseStages.REPLACED}]",
- sort="adoption",
- environment=self.environment.name,
- )
- self.assert_expected_versions(response, [replaced_release, not_adopted_release])
- response = self.get_response(
- self.organization.slug,
- query=f"{RELEASE_STAGE_ALIAS}:invalid_stage",
- environment=self.environment.name,
- )
- assert response.status_code == 400
- response = self.get_response(
- self.organization.slug,
- query=f"{RELEASE_STAGE_ALIAS}:{ReleaseStages.ADOPTED}",
- # No environment
- )
- assert response.status_code == 400
- def test_project_permissions(self):
- user = self.create_user(is_staff=False, is_superuser=False)
- org = self.create_organization()
- org.flags.allow_joinleave = False
- org.save()
- team1 = self.create_team(organization=org)
- team2 = self.create_team(organization=org)
- project1 = self.create_project(teams=[team1], organization=org)
- project2 = self.create_project(teams=[team2], organization=org)
- self.create_member(teams=[team1], user=user, organization=org)
- self.login_as(user=user)
- release1 = Release.objects.create(
- organization_id=org.id, version="1", date_added=datetime(2013, 8, 13, 3, 8, 24, 880386)
- )
- release1.add_project(project1)
- release2 = Release.objects.create(
- organization_id=org.id, version="2", date_added=datetime(2013, 8, 14, 3, 8, 24, 880386)
- )
- release2.add_project(project2)
- release3 = Release.objects.create(
- organization_id=org.id,
- version="3",
- date_added=datetime(2013, 8, 12, 3, 8, 24, 880386),
- date_released=datetime(2013, 8, 15, 3, 8, 24, 880386),
- )
- release3.add_project(project1)
- ax = access.from_user(user, org)
- assert ax.has_projects_access([project1])
- assert ax.has_project_membership(project1)
- assert not ax.has_project_membership(project2)
- response = self.get_success_response(org.slug)
- self.assert_expected_versions(response, [release1, release3])
- def test_project_permissions_open_access(self):
- user = self.create_user(is_staff=False, is_superuser=False)
- org = self.create_organization()
- org.flags.allow_joinleave = True
- org.save()
- team1 = self.create_team(organization=org)
- team2 = self.create_team(organization=org)
- project1 = self.create_project(teams=[team1], organization=org)
- project2 = self.create_project(teams=[team2], organization=org)
- self.create_member(teams=[team1], user=user, organization=org)
- self.login_as(user=user)
- release1 = Release.objects.create(
- organization_id=org.id, version="1", date_added=datetime(2013, 8, 13, 3, 8, 24, 880386)
- )
- release1.add_project(project1)
- release2 = Release.objects.create(
- organization_id=org.id, version="2", date_added=datetime(2013, 8, 14, 3, 8, 24, 880386)
- )
- release2.add_project(project2)
- release3 = Release.objects.create(
- organization_id=org.id,
- version="3",
- date_added=datetime(2013, 8, 12, 3, 8, 24, 880386),
- date_released=datetime(2013, 8, 15, 3, 8, 24, 880386),
- )
- release3.add_project(project1)
- ax = access.from_user(user, org)
- assert ax.has_projects_access([project1, project2])
- assert ax.has_project_membership(project1)
- assert not ax.has_project_membership(project2)
- response = self.get_success_response(org.slug)
- self.assert_expected_versions(response, [release1, release3])
- def test_all_projects_parameter(self):
- user = self.create_user(is_staff=False, is_superuser=False)
- org = self.create_organization()
- org.flags.allow_joinleave = True
- org.save()
- team1 = self.create_team(organization=org)
- team2 = self.create_team(organization=org)
- project1 = self.create_project(teams=[team1], organization=org)
- project2 = self.create_project(teams=[team2], organization=org)
- self.create_member(teams=[team1], user=user, organization=org)
- self.login_as(user=user)
- release1 = Release.objects.create(
- organization_id=org.id, version="1", date_added=datetime(2013, 8, 13, 3, 8, 24, 880386)
- )
- release1.add_project(project1)
- release2 = Release.objects.create(
- organization_id=org.id, version="2", date_added=datetime(2013, 8, 14, 3, 8, 24, 880386)
- )
- release2.add_project(project2)
- response = self.get_success_response(org.slug, project=[-1])
- self.assert_expected_versions(response, [release2, release1])
- def test_new_org(self):
- user = self.create_user(is_staff=False, is_superuser=False)
- org = self.organization
- team = self.create_team(organization=org)
- self.create_member(teams=[team], user=user, organization=org)
- self.login_as(user=user)
- response = self.get_success_response(org.slug)
- self.assert_expected_versions(response, [])
- def test_archive_release(self):
- self.login_as(user=self.user)
- url = reverse(
- "sentry-api-0-organization-releases",
- kwargs={"organization_slug": self.organization.slug},
- )
- # test legacy status value of None (=open)
- self.release.status = None
- self.release.save()
- response = self.client.get(url, format="json")
- assert response.status_code == 200, response.content
- assert len(response.data) == 1
- (release_data,) = response.data
- response = self.client.post(
- url,
- format="json",
- data={
- "version": release_data["version"],
- "projects": [x["slug"] for x in release_data["projects"]],
- "status": "archived",
- },
- )
- assert response.status_code == 208, response.content
- response = self.client.get(url, format="json")
- assert response.status_code == 200, response.content
- assert len(response.data) == 0
- response = self.client.get(url + "?status=archived", format="json")
- assert response.status_code == 200, response.content
- assert len(response.data) == 1
- response = self.client.get(url + "?status=", format="json")
- assert response.status_code == 200, response.content
- assert len(response.data) == 1
- @region_silo_test(stable=True)
- class OrganizationReleasesStatsTest(APITestCase):
- endpoint = "sentry-api-0-organization-releases-stats"
- def setUp(self):
- self.project1 = self.create_project(teams=[self.team], organization=self.organization)
- self.project2 = self.create_project(teams=[self.team], organization=self.organization)
- self.project3 = self.create_project(teams=[self.team], organization=self.organization)
- self.login_as(user=self.user)
- def test_simple(self):
- release1 = Release.objects.create(
- organization_id=self.organization.id,
- version="1",
- date_added=datetime(2013, 8, 13, 3, 8, 24, 880386, tzinfo=pytz.UTC),
- )
- release1.add_project(self.project1)
- release2 = Release.objects.create(
- organization_id=self.organization.id,
- version="2",
- date_added=datetime(2013, 8, 12, 3, 8, 24, 880386, tzinfo=pytz.UTC),
- date_released=datetime(2013, 8, 15, 3, 8, 24, 880386, tzinfo=pytz.UTC),
- )
- release2.add_project(self.project2)
- release3 = Release.objects.create(
- organization_id=self.organization.id,
- version="3",
- date_added=datetime(2013, 8, 14, 3, 8, 24, 880386, tzinfo=pytz.UTC),
- )
- release3.add_project(self.project3)
- url = reverse(
- "sentry-api-0-organization-releases-stats",
- kwargs={"organization_slug": self.organization.slug},
- )
- response = self.client.get(url, format="json")
- assert response.status_code == 200, response.content
- assert len(response.data) == 3
- assert response.data[0]["version"] == release3.version
- assert response.data[0]["date"] == release3.date_added
- assert response.data[1]["version"] == release1.version
- assert response.data[1]["date"] == release1.date_added
- assert response.data[2]["version"] == release2.version
- assert response.data[2]["date"] == release2.date_added
- def test_release_list_order_by_date_added(self):
- """
- Test that ensures that by relying on the default date sorting, releases
- will only be sorted according to `Release.date_added`, and
- `Release.date_released` should have no effect whatsoever on that order
- """
- user = self.create_user(is_staff=False, is_superuser=False)
- org = self.organization
- org.flags.allow_joinleave = False
- org.save()
- team = self.create_team(organization=org)
- project = self.create_project(teams=[team], organization=org)
- self.create_member(teams=[team], user=user, organization=org)
- self.login_as(user=user)
- release6 = Release.objects.create(
- organization_id=org.id,
- version="6",
- date_added=datetime(2013, 8, 10, 3, 8, 24, 880386),
- date_released=datetime(2013, 8, 20, 3, 8, 24, 880386),
- )
- release6.add_project(project)
- release7 = Release.objects.create(
- organization_id=org.id,
- version="7",
- date_added=datetime(2013, 8, 12, 3, 8, 24, 880386),
- date_released=datetime(2013, 8, 18, 3, 8, 24, 880386),
- )
- release7.add_project(project)
- release8 = Release.objects.create(
- organization_id=org.id,
- version="8",
- date_added=datetime(2013, 8, 14, 3, 8, 24, 880386),
- date_released=datetime(2013, 8, 16, 3, 8, 24, 880386),
- )
- release8.add_project(project)
- url = reverse(
- "sentry-api-0-organization-releases-stats",
- kwargs={"organization_slug": self.organization.slug},
- )
- response = self.client.get(url, format="json")
- assert response.status_code == 200, response.content
- assert len(response.data) == 3
- assert response.data[0]["version"] == release8.version
- assert response.data[1]["version"] == release7.version
- assert response.data[2]["version"] == release6.version
- def test_with_adoption_stages(self):
- user = self.create_user(is_staff=False, is_superuser=False)
- org = self.organization
- org.save()
- team1 = self.create_team(organization=org)
- project1 = self.create_project(teams=[team1], organization=org)
- self.create_member(teams=[team1], user=user, organization=org)
- self.login_as(user=user)
- release1 = Release.objects.create(
- organization_id=org.id, version="1", date_added=datetime(2013, 8, 13, 3, 8, 24, 880386)
- )
- release1.add_project(project1)
- url = reverse("sentry-api-0-organization-releases", kwargs={"organization_slug": org.slug})
- response = self.client.get(url, format="json")
- assert response.status_code == 200, response.content
- assert len(response.data) == 1
- # Not returned because we don't have `adoptionStages=1`.
- assert "adoptionStages" not in response.data[0]
- response = self.client.get(f"{url}?adoptionStages=1", format="json")
- assert response.status_code == 200, response.content
- assert len(response.data) == 1
- assert "adoptionStages" in response.data[0]
- def test_semver_filter(self):
- self.login_as(user=self.user)
- release_1 = self.create_release(version="test@1.2.4")
- release_2 = self.create_release(version="test@1.2.3")
- release_3 = self.create_release(version="test2@1.2.5")
- self.create_release(version="some.release")
- response = self.get_success_response(self.organization.slug, query=f"{SEMVER_ALIAS}:>1.2.3")
- assert [r["version"] for r in response.data] == [release_3.version, release_1.version]
- response = self.get_success_response(
- self.organization.slug, query=f"{SEMVER_ALIAS}:>=1.2.3"
- )
- assert [r["version"] for r in response.data] == [
- release_3.version,
- release_2.version,
- release_1.version,
- ]
- response = self.get_success_response(self.organization.slug, query=f"{SEMVER_ALIAS}:1.2.*")
- assert [r["version"] for r in response.data] == [
- release_3.version,
- release_2.version,
- release_1.version,
- ]
- response = self.get_success_response(self.organization.slug, query=f"{SEMVER_ALIAS}:2.2.1")
- assert [r["version"] for r in response.data] == []
- response = self.get_success_response(
- self.organization.slug, query=f"{SEMVER_PACKAGE_ALIAS}:test2"
- )
- assert [r["version"] for r in response.data] == [release_3.version]
- response = self.get_success_response(
- self.organization.slug, query=f"{SEMVER_PACKAGE_ALIAS}:test"
- )
- assert [r["version"] for r in response.data] == [release_2.version, release_1.version]
- def test_release_stage_filter(self):
- self.login_as(user=self.user)
- response = self.get_success_response(
- self.organization.slug,
- query=f"{RELEASE_STAGE_ALIAS}:adopted",
- environment=self.environment.name,
- )
- assert [r["version"] for r in response.data] == []
- replaced_release = self.create_release(version="replaced_release")
- adopted_release = self.create_release(version="adopted_release")
- not_adopted_release = self.create_release(version="not_adopted_release")
- ReleaseProjectEnvironment.objects.create(
- project_id=self.project.id,
- release_id=adopted_release.id,
- environment_id=self.environment.id,
- adopted=timezone.now(),
- )
- ReleaseProjectEnvironment.objects.create(
- project_id=self.project.id,
- release_id=replaced_release.id,
- environment_id=self.environment.id,
- adopted=timezone.now(),
- unadopted=timezone.now(),
- )
- ReleaseProjectEnvironment.objects.create(
- project_id=self.project.id,
- release_id=not_adopted_release.id,
- environment_id=self.environment.id,
- )
- response = self.get_success_response(
- self.organization.slug,
- query=f"{RELEASE_STAGE_ALIAS}:{ReleaseStages.ADOPTED}",
- environment=self.environment.name,
- )
- assert [r["version"] for r in response.data] == [adopted_release.version]
- response = self.get_success_response(
- self.organization.slug,
- query=f"{RELEASE_STAGE_ALIAS}:{ReleaseStages.LOW_ADOPTION}",
- environment=self.environment.name,
- )
- assert [r["version"] for r in response.data] == [not_adopted_release.version]
- response = self.get_success_response(
- self.organization.slug,
- query=f"{RELEASE_STAGE_ALIAS}:{ReleaseStages.REPLACED}",
- environment=self.environment.name,
- )
- assert [r["version"] for r in response.data] == [replaced_release.version]
- response = self.get_success_response(
- self.organization.slug,
- query=f"{RELEASE_STAGE_ALIAS}:[{ReleaseStages.ADOPTED},{ReleaseStages.REPLACED}]",
- environment=self.environment.name,
- )
- assert [r["version"] for r in response.data] == [
- adopted_release.version,
- replaced_release.version,
- ]
- response = self.get_success_response(
- self.organization.slug,
- query=f"{RELEASE_STAGE_ALIAS}:[{ReleaseStages.LOW_ADOPTION}]",
- environment=self.environment.name,
- )
- assert [r["version"] for r in response.data] == [not_adopted_release.version]
- response = self.get_response(
- self.organization.slug,
- query=f"{RELEASE_STAGE_ALIAS}:invalid_stage",
- environment=self.environment.name,
- )
- assert response.status_code == 400
- response = self.get_response(
- self.organization.slug,
- query=f"{RELEASE_STAGE_ALIAS}:{ReleaseStages.ADOPTED}",
- # No environment
- )
- assert response.status_code == 400
- def test_multi_project_release_gets_filtered(self):
- multi_project_release = self.create_release(version="multi_project_release")
- single_project_release = self.create_release(version="single_project_release")
- project2 = self.create_project(teams=[self.team], organization=self.organization)
- # One project not adopted
- ReleaseProjectEnvironment.objects.create(
- project_id=self.project.id,
- release_id=multi_project_release.id,
- environment_id=self.environment.id,
- )
- # One project adopted
- ReleaseProjectEnvironment.objects.create(
- project_id=project2.id,
- release_id=multi_project_release.id,
- environment_id=self.environment.id,
- adopted=timezone.now(),
- )
- ReleaseProjectEnvironment.objects.create(
- project_id=self.project.id,
- release_id=single_project_release.id,
- environment_id=self.environment.id,
- adopted=timezone.now(),
- )
- # Filtering to self.environment.name and self.project with release.stage:adopted should NOT return multi_project_release.
- response = self.get_success_response(
- self.organization.slug,
- project=self.project.id,
- environment=self.environment.name,
- query=f"{RELEASE_STAGE_ALIAS}:adopted",
- )
- assert [r["version"] for r in response.data] == [single_project_release.version]
- response = self.get_success_response(
- self.organization.slug,
- environment=self.environment.name,
- query=f"{RELEASE_STAGE_ALIAS}:adopted",
- )
- assert [r["version"] for r in response.data] == [
- single_project_release.version,
- multi_project_release.version,
- ]
- def test_query_filter(self):
- self.login_as(user=self.user)
- release = self.create_release(
- self.project, version="foobar", date_added=datetime(2013, 8, 13, 3, 8, 24, 880386)
- )
- self.create_release(
- self.project, version="sdfsdfsdf", date_added=datetime(2013, 8, 13, 3, 8, 24, 880386)
- )
- response = self.get_success_response(self.organization.slug, query="oob")
- assert [r["version"] for r in response.data] == [release.version]
- response = self.get_success_response(self.organization.slug, query="baz")
- assert [r["version"] for r in response.data] == []
- response = self.get_success_response(self.organization.slug, query="release:*oob*")
- assert [r["version"] for r in response.data] == [release.version]
- response = self.get_success_response(self.organization.slug, query="release:foob*")
- assert [r["version"] for r in response.data] == [release.version]
- response = self.get_success_response(self.organization.slug, query="release:*bar")
- assert [r["version"] for r in response.data] == [release.version]
- response = self.get_success_response(self.organization.slug, query="release:foobar")
- assert [r["version"] for r in response.data] == [release.version]
- response = self.get_success_response(self.organization.slug, query="release:*baz*")
- assert [r["version"] for r in response.data] == []
- @region_silo_test(stable=True)
- class OrganizationReleaseCreateTest(APITestCase):
- def test_empty_release_version(self):
- user = self.create_user(is_staff=False, is_superuser=False)
- org = self.create_organization()
- org.flags.allow_joinleave = False
- org.save()
- team = self.create_team(organization=org)
- project = self.create_project(name="foo", organization=org, teams=[team])
- project2 = self.create_project(name="bar", organization=org, teams=[team])
- self.create_member(teams=[team], user=user, organization=org)
- self.login_as(user=user)
- url = reverse("sentry-api-0-organization-releases", kwargs={"organization_slug": org.slug})
- response = self.client.post(
- url, data={"version": "", "projects": [project.slug, project2.slug]}
- )
- assert response.status_code == 400
- def test_minimal(self):
- user = self.create_user(is_staff=False, is_superuser=False)
- org = self.create_organization()
- org.flags.allow_joinleave = False
- org.save()
- team = self.create_team(organization=org)
- project = self.create_project(name="foo", organization=org, teams=[team])
- project2 = self.create_project(name="bar", organization=org, teams=[team])
- self.create_member(teams=[team], user=user, organization=org)
- self.login_as(user=user)
- url = reverse("sentry-api-0-organization-releases", kwargs={"organization_slug": org.slug})
- response = self.client.post(
- url,
- data={"version": "1.2.1", "projects": [project.slug, project2.slug]},
- HTTP_USER_AGENT="sentry-cli/2.77.4",
- )
- assert response.status_code == 201, response.content
- assert response.data["version"]
- release = Release.objects.get(
- version=response.data["version"], user_agent="sentry-cli/2.77.4"
- )
- assert not release.owner_id
- assert release.organization == org
- assert ReleaseProject.objects.filter(release=release, project=project).exists()
- assert ReleaseProject.objects.filter(release=release, project=project2).exists()
- def test_duplicate(self):
- user = self.create_user(is_staff=False, is_superuser=False)
- org = self.create_organization()
- org.flags.allow_joinleave = False
- org.save()
- repo = Repository.objects.create(
- provider="dummy", name="my-org/my-repository", organization_id=org.id
- )
- team = self.create_team(organization=org)
- project = self.create_project(name="foo", organization=org, teams=[team])
- self.create_member(teams=[team], user=user, organization=org)
- self.login_as(user=user)
- release = Release.objects.create(version="1.2.1", organization=org)
- url = reverse("sentry-api-0-organization-releases", kwargs={"organization_slug": org.slug})
- with self.tasks():
- response = self.client.post(
- url,
- data={
- "version": "1.2.1",
- "projects": [project.slug],
- "refs": [
- {
- "repository": "my-org/my-repository",
- "commit": "a" * 40,
- "previousCommit": "c" * 40,
- }
- ],
- },
- )
- release_commits1 = list(
- ReleaseCommit.objects.filter(release=release)
- .order_by("order")
- .values_list("commit__key", flat=True)
- )
- # check that commits are overwritten
- assert release_commits1 == [
- "62de626b7c7cfb8e77efb4273b1a3df4123e6216",
- "58de626b7c7cfb8e77efb4273b1a3df4123e6345",
- "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
- ]
- # should be 201 because project was added
- assert response.status_code == 201, response.content
- with self.tasks():
- with patch.object(DummyRepositoryProvider, "compare_commits") as mock_compare_commits:
- mock_compare_commits.return_value = [
- {"id": "c" * 40, "repository": repo.name},
- {"id": "d" * 40, "repository": repo.name},
- {"id": "a" * 40, "repository": repo.name},
- ]
- response2 = self.client.post(
- url,
- data={
- "version": "1.2.1",
- "projects": [project.slug],
- "refs": [
- {
- "repository": "my-org/my-repository",
- "commit": "a" * 40,
- "previousCommit": "b" * 40,
- }
- ],
- },
- )
- release_commits2 = list(
- ReleaseCommit.objects.filter(release=release)
- .order_by("order")
- .values_list("commit__key", flat=True)
- )
- # check that commits are overwritten
- assert release_commits2 == [
- "cccccccccccccccccccccccccccccccccccccccc",
- "dddddddddddddddddddddddddddddddddddddddd",
- "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
- ]
- assert response2.status_code == 208, response.content
- assert Release.objects.filter(version="1.2.1", organization=org).count() == 1
- # make sure project was added
- assert ReleaseProject.objects.filter(release=release, project=project).exists()
- def test_activity(self):
- user = self.create_user(is_staff=False, is_superuser=False)
- org = self.create_organization()
- org.flags.allow_joinleave = False
- org.save()
- team = self.create_team(organization=org)
- project = self.create_project(name="foo", organization=org, teams=[team])
- project2 = self.create_project(name="bar", organization=org, teams=[team])
- self.create_member(teams=[team], user=user, organization=org)
- self.login_as(user=user)
- release = Release.objects.create(
- version="1.2.1", date_released=datetime.utcnow(), organization=org
- )
- release.add_project(project)
- url = reverse("sentry-api-0-organization-releases", kwargs={"organization_slug": org.slug})
- response = self.client.post(url, data={"version": "1.2.1", "projects": [project.slug]})
- assert response.status_code == 208, response.content
- response = self.client.post(
- url, data={"version": "1.2.1", "projects": [project.slug, project2.slug]}
- )
- # should be 201 because 1 project was added
- assert response.status_code == 201, response.content
- assert not Activity.objects.filter(
- type=ActivityType.RELEASE.value, project=project, ident=release.version
- ).exists()
- assert Activity.objects.filter(
- type=ActivityType.RELEASE.value, project=project2, ident=release.version
- ).exists()
- def test_activity_with_long_release(self):
- user = self.create_user(is_staff=False, is_superuser=False)
- org = self.create_organization()
- org.flags.allow_joinleave = False
- org.save()
- team = self.create_team(organization=org)
- project = self.create_project(name="foo", organization=org, teams=[team])
- project2 = self.create_project(name="bar", organization=org, teams=[team])
- self.create_member(teams=[team], user=user, organization=org)
- self.login_as(user=user)
- release = Release.objects.create(
- version="x" * 65, date_released=datetime.utcnow(), organization=org
- )
- release.add_project(project)
- url = reverse("sentry-api-0-organization-releases", kwargs={"organization_slug": org.slug})
- response = self.client.post(url, data={"version": "x" * 65, "projects": [project.slug]})
- assert response.status_code == 208, response.content
- response = self.client.post(
- url, data={"version": "x" * 65, "projects": [project.slug, project2.slug]}
- )
- # should be 201 because 1 project was added
- assert response.status_code == 201, response.content
- assert not Activity.objects.filter(
- type=ActivityType.RELEASE.value, project=project, ident=release.version[:64]
- ).exists()
- assert Activity.objects.filter(
- type=ActivityType.RELEASE.value, project=project2, ident=release.version[:64]
- ).exists()
- def test_version_whitespace(self):
- user = self.create_user(is_staff=False, is_superuser=False)
- org = self.create_organization()
- org.flags.allow_joinleave = False
- org.save()
- team = self.create_team(organization=org)
- project = self.create_project(name="foo", organization=org, teams=[team])
- self.create_member(teams=[team], user=user, organization=org)
- self.login_as(user=user)
- url = reverse("sentry-api-0-organization-releases", kwargs={"organization_slug": org.slug})
- response = self.client.post(url, data={"version": "1.2.3\n", "projects": [project.slug]})
- assert response.status_code == 400, response.content
- response = self.client.post(url, data={"version": "\n1.2.3", "projects": [project.slug]})
- assert response.status_code == 400, response.content
- response = self.client.post(url, data={"version": "1.\n2.3", "projects": [project.slug]})
- assert response.status_code == 400, response.content
- response = self.client.post(url, data={"version": "1.2.3\f", "projects": [project.slug]})
- assert response.status_code == 400, response.content
- response = self.client.post(url, data={"version": "1.2.3\t", "projects": [project.slug]})
- assert response.status_code == 400, response.content
- response = self.client.post(url, data={"version": "1.2.3+dev", "projects": [project.slug]})
- assert response.status_code == 201, response.content
- assert response.data["version"] == "1.2.3+dev"
- release = Release.objects.get(organization_id=org.id, version=response.data["version"])
- assert not release.owner_id
- def test_features(self):
- user = self.create_user(is_staff=False, is_superuser=False)
- org = self.create_organization()
- org.flags.allow_joinleave = False
- org.save()
- team = self.create_team(organization=org)
- project = self.create_project(name="foo", organization=org, teams=[team])
- self.create_member(teams=[team], user=user, organization=org)
- self.create_member(teams=[team], user=self.user, organization=org)
- self.login_as(user=user)
- url = reverse("sentry-api-0-organization-releases", kwargs={"organization_slug": org.slug})
- response = self.client.post(
- url, data={"version": "1.2.1", "owner": self.user.email, "projects": [project.slug]}
- )
- assert response.status_code == 201, response.content
- assert response.data["version"]
- release = Release.objects.get(organization_id=org.id, version=response.data["version"])
- assert release.owner_id == self.user.id
- def test_commits(self):
- user = self.create_user(is_staff=False, is_superuser=False)
- org = self.create_organization()
- org.flags.allow_joinleave = False
- org.save()
- team = self.create_team(organization=org)
- project = self.create_project(name="foo", organization=org, teams=[team])
- self.create_member(teams=[team], user=user, organization=org)
- self.login_as(user=user)
- url = reverse("sentry-api-0-organization-releases", kwargs={"organization_slug": org.slug})
- response = self.client.post(
- url,
- data={
- "version": "1.2.1",
- "commits": [{"id": "a" * 40}, {"id": "b" * 40}],
- "projects": [project.slug],
- },
- )
- assert response.status_code == 201, (response.status_code, response.content)
- assert response.data["version"]
- release = Release.objects.get(organization_id=org.id, version=response.data["version"])
- rc_list = list(
- ReleaseCommit.objects.filter(release=release)
- .select_related("commit", "commit__author")
- .order_by("order")
- )
- assert len(rc_list) == 2
- for rc in rc_list:
- assert rc.organization_id
- @patch("sentry.tasks.commits.fetch_commits")
- def test_commits_from_provider(self, mock_fetch_commits):
- user = self.create_user(is_staff=False, is_superuser=False)
- org = self.create_organization()
- org.flags.allow_joinleave = False
- org.save()
- repo = Repository.objects.create(
- organization_id=org.id, name="example/example", provider="dummy"
- )
- repo2 = Repository.objects.create(
- organization_id=org.id, name="example/example2", provider="dummy"
- )
- team = self.create_team(organization=org)
- project = self.create_project(name="foo", organization=org, teams=[team])
- self.create_member(teams=[team], user=user, organization=org)
- self.login_as(user=user)
- url = reverse("sentry-api-0-organization-releases", kwargs={"organization_slug": org.slug})
- self.client.post(
- url,
- data={
- "version": "1",
- "refs": [
- {"commit": "0" * 40, "repository": repo.name},
- {"commit": "0" * 40, "repository": repo2.name},
- ],
- "projects": [project.slug],
- },
- )
- response = self.client.post(
- url,
- data={
- "version": "1.2.1",
- "refs": [
- {"commit": "a" * 40, "repository": repo.name},
- {"commit": "b" * 40, "repository": repo2.name},
- ],
- "projects": [project.slug],
- },
- )
- assert response.status_code == 201
- mock_fetch_commits.apply_async.assert_called_with(
- kwargs={
- "release_id": Release.objects.get(version="1.2.1", organization=org).id,
- "user_id": user.id,
- "refs": [
- {"commit": "a" * 40, "repository": repo.name},
- {"commit": "b" * 40, "repository": repo2.name},
- ],
- "prev_release_id": Release.objects.get(version="1", organization=org).id,
- }
- )
- @patch("sentry.tasks.commits.fetch_commits")
- def test_commits_from_provider_deprecated_head_commits(self, mock_fetch_commits):
- user = self.create_user(is_staff=False, is_superuser=False)
- org = self.create_organization()
- org.flags.allow_joinleave = False
- org.save()
- repo = Repository.objects.create(
- organization_id=org.id, name="example/example", provider="dummy"
- )
- repo2 = Repository.objects.create(
- organization_id=org.id, name="example/example2", provider="dummy"
- )
- team = self.create_team(organization=org)
- project = self.create_project(name="foo", organization=org, teams=[team])
- self.create_member(teams=[team], user=user, organization=org)
- self.login_as(user=user)
- url = reverse("sentry-api-0-organization-releases", kwargs={"organization_slug": org.slug})
- self.client.post(
- url,
- data={
- "version": "1",
- "headCommits": [
- {"currentId": "0" * 40, "repository": repo.name},
- {"currentId": "0" * 40, "repository": repo2.name},
- ],
- "projects": [project.slug],
- },
- )
- response = self.client.post(
- url,
- data={
- "version": "1.2.1",
- "headCommits": [
- {"currentId": "a" * 40, "repository": repo.name},
- {"currentId": "b" * 40, "repository": repo2.name},
- ],
- "projects": [project.slug],
- },
- format="json",
- )
- mock_fetch_commits.apply_async.assert_called_with(
- kwargs={
- "release_id": Release.objects.get(version="1.2.1", organization=org).id,
- "user_id": user.id,
- "refs": [
- {"commit": "a" * 40, "repository": repo.name, "previousCommit": None},
- {"commit": "b" * 40, "repository": repo2.name, "previousCommit": None},
- ],
- "prev_release_id": Release.objects.get(version="1", organization=org).id,
- }
- )
- assert response.status_code == 201
- def test_commits_lock_conflict(self):
- user = self.create_user(is_staff=False, is_superuser=False)
- org = self.create_organization()
- org.flags.allow_joinleave = False
- org.save()
- team = self.create_team(organization=org)
- project = self.create_project(name="foo", organization=org, teams=[team])
- self.create_member(teams=[team], user=user, organization=org)
- self.login_as(user=user)
- # Simulate a concurrent request by using an existing release
- # that has its commit lock taken out.
- release = self.create_release(project, self.user, version="1.2.1")
- lock = locks.get(Release.get_lock_key(org.id, release.id), duration=10, name="release")
- lock.acquire()
- url = reverse("sentry-api-0-organization-releases", kwargs={"organization_slug": org.slug})
- response = self.client.post(
- url,
- data={
- "version": release.version,
- "commits": [{"id": "a" * 40}, {"id": "b" * 40}],
- "projects": [project.slug],
- },
- )
- assert response.status_code == 409, (response.status_code, response.content)
- assert "Release commits" in response.data["detail"]
- def test_bad_project_slug(self):
- user = self.create_user(is_staff=False, is_superuser=False)
- org = self.create_organization()
- org.flags.allow_joinleave = False
- org.save()
- team = self.create_team(organization=org)
- project = self.create_project(name="foo", organization=org, teams=[team])
- self.create_member(teams=[team], user=user, organization=org)
- self.login_as(user=user)
- url = reverse("sentry-api-0-organization-releases", kwargs={"organization_slug": org.slug})
- response = self.client.post(
- url, data={"version": "1.2.1", "projects": [project.slug, "banana"]}
- )
- assert response.status_code == 400
- assert b"Invalid project slugs" in response.content
- def test_project_permissions(self):
- user = self.create_user(is_staff=False, is_superuser=False)
- org = self.create_organization()
- org.flags.allow_joinleave = False
- org.save()
- team1 = self.create_team(organization=org)
- team2 = self.create_team(organization=org)
- project1 = self.create_project(teams=[team1], organization=org)
- project2 = self.create_project(teams=[team2], organization=org)
- self.create_member(teams=[team1], user=user, organization=org)
- self.login_as(user=user)
- release1 = Release.objects.create(
- organization_id=org.id, version="1", date_added=datetime(2013, 8, 13, 3, 8, 24, 880386)
- )
- release1.add_project(project1)
- release2 = Release.objects.create(
- organization_id=org.id, version="2", date_added=datetime(2013, 8, 14, 3, 8, 24, 880386)
- )
- release2.add_project(project2)
- release3 = Release.objects.create(
- organization_id=org.id,
- version="3",
- date_added=datetime(2013, 8, 12, 3, 8, 24, 880386),
- date_released=datetime(2013, 8, 15, 3, 8, 24, 880386),
- )
- release3.add_project(project1)
- url = reverse("sentry-api-0-organization-releases", kwargs={"organization_slug": org.slug})
- response = self.client.post(
- url, data={"version": "1.2.1", "projects": [project1.slug, project2.slug]}
- )
- assert response.status_code == 400
- assert b"Invalid project slugs" in response.content
- response = self.client.post(url, data={"version": "1.2.1", "projects": [project1.slug]})
- assert response.status_code == 201, response.content
- def test_api_key(self):
- org = self.create_organization()
- org.flags.allow_joinleave = False
- org.save()
- org2 = self.create_organization()
- team1 = self.create_team(organization=org)
- project1 = self.create_project(teams=[team1], organization=org)
- release1 = Release.objects.create(
- organization_id=org.id, version="1", date_added=datetime(2013, 8, 13, 3, 8, 24, 880386)
- )
- release1.add_project(project1)
- url = reverse("sentry-api-0-organization-releases", kwargs={"organization_slug": org.slug})
- # test right org, wrong permissions level
- with assume_test_silo_mode(SiloMode.CONTROL):
- bad_api_key = ApiKey.objects.create(organization_id=org.id, scope_list=["project:read"])
- response = self.client.post(
- url,
- data={"version": "1.2.1", "projects": [project1.slug]},
- HTTP_AUTHORIZATION=self.create_basic_auth_header(bad_api_key.key),
- )
- assert response.status_code == 403
- # test wrong org, right permissions level
- with assume_test_silo_mode(SiloMode.CONTROL):
- wrong_org_api_key = ApiKey.objects.create(
- organization_id=org2.id, scope_list=["project:write"]
- )
- response = self.client.post(
- url,
- data={"version": "1.2.1", "projects": [project1.slug]},
- HTTP_AUTHORIZATION=self.create_basic_auth_header(wrong_org_api_key.key),
- )
- assert response.status_code == 403
- # test right org, right permissions level
- with assume_test_silo_mode(SiloMode.CONTROL):
- good_api_key = ApiKey.objects.create(
- organization_id=org.id, scope_list=["project:write"]
- )
- response = self.client.post(
- url,
- data={"version": "1.2.1", "projects": [project1.slug]},
- HTTP_AUTHORIZATION=self.create_basic_auth_header(good_api_key.key),
- )
- assert response.status_code == 201, response.content
- def test_org_auth_token(self):
- org = self.create_organization()
- org.flags.allow_joinleave = False
- org.save()
- org2 = self.create_organization()
- team1 = self.create_team(organization=org)
- project1 = self.create_project(teams=[team1], organization=org)
- release1 = Release.objects.create(
- organization_id=org.id, version="1", date_added=datetime(2013, 8, 13, 3, 8, 24, 880386)
- )
- release1.add_project(project1)
- url = reverse("sentry-api-0-organization-releases", kwargs={"organization_slug": org.slug})
- # test right org, wrong permissions level
- with assume_test_silo_mode(SiloMode.CONTROL):
- bad_token_str = generate_token(org.slug, "")
- OrgAuthToken.objects.create(
- organization_id=org.id,
- name="token 1",
- token_hashed=hash_token(bad_token_str),
- token_last_characters="ABCD",
- scope_list=[],
- date_last_used=None,
- )
- response = self.client.post(
- url,
- data={"version": "1.2.1", "projects": [project1.slug]},
- HTTP_AUTHORIZATION=f"Bearer {bad_token_str}",
- )
- assert response.status_code == 403
- # test wrong org, right permissions level
- with assume_test_silo_mode(SiloMode.CONTROL):
- wrong_org_token_str = generate_token(org2.slug, "")
- OrgAuthToken.objects.create(
- organization_id=org2.id,
- name="token 1",
- token_hashed=hash_token(wrong_org_token_str),
- token_last_characters="ABCD",
- scope_list=["org:ci"],
- date_last_used=None,
- )
- response = self.client.post(
- url,
- data={"version": "1.2.1", "projects": [project1.slug]},
- HTTP_AUTHORIZATION=f"Bearer {wrong_org_token_str}",
- )
- assert response.status_code == 403
- # test right org, right permissions level
- with assume_test_silo_mode(SiloMode.CONTROL):
- good_token_str = generate_token(org.slug, "")
- OrgAuthToken.objects.create(
- organization_id=org.id,
- name="token 1",
- token_hashed=hash_token(good_token_str),
- token_last_characters="ABCD",
- scope_list=["org:ci"],
- date_last_used=None,
- )
- with outbox_runner():
- response = self.client.post(
- url,
- data={"version": "1.2.1", "projects": [project1.slug]},
- HTTP_AUTHORIZATION=f"Bearer {good_token_str}",
- )
- assert response.status_code == 201, response.content
- # Make sure org token usage was updated
- with assume_test_silo_mode(SiloMode.CONTROL):
- org_token = OrgAuthToken.objects.get(token_hashed=hash_token(good_token_str))
- assert org_token.date_last_used is not None
- assert org_token.project_last_used_id == project1.id
- @patch("sentry.tasks.commits.fetch_commits")
- def test_api_token(self, mock_fetch_commits):
- user = self.create_user(is_staff=False, is_superuser=False)
- org = self.create_organization()
- org.flags.allow_joinleave = False
- org.save()
- repo = Repository.objects.create(
- organization_id=org.id, name="getsentry/sentry", provider="dummy"
- )
- repo2 = Repository.objects.create(
- organization_id=org.id, name="getsentry/sentry-plugins", provider="dummy"
- )
- with assume_test_silo_mode(SiloMode.CONTROL):
- api_token = ApiToken.objects.create(user=user, scope_list=["project:releases"])
- team1 = self.create_team(organization=org)
- self.create_member(teams=[team1], user=user, organization=org)
- project1 = self.create_project(teams=[team1], organization=org)
- release1 = Release.objects.create(
- organization_id=org.id, version="1", date_added=datetime(2013, 8, 13, 3, 8, 24, 880386)
- )
- release1.add_project(project1)
- url = reverse("sentry-api-0-organization-releases", kwargs={"organization_slug": org.slug})
- response = self.client.post(
- url,
- data={
- "version": "1.2.1",
- "refs": [
- {"commit": "a" * 40, "repository": repo.name, "previousCommit": "c" * 40},
- {"commit": "b" * 40, "repository": repo2.name},
- ],
- "projects": [project1.slug],
- },
- HTTP_AUTHORIZATION=f"Bearer {api_token.token}",
- )
- mock_fetch_commits.apply_async.assert_called_with(
- kwargs={
- "release_id": Release.objects.get(version="1.2.1", organization=org).id,
- "user_id": user.id,
- "refs": [
- {"commit": "a" * 40, "repository": repo.name, "previousCommit": "c" * 40},
- {"commit": "b" * 40, "repository": repo2.name},
- ],
- "prev_release_id": release1.id,
- }
- )
- assert response.status_code == 201
- def test_bad_repo_name(self):
- user = self.create_user(is_staff=False, is_superuser=False)
- org = self.create_organization()
- org.flags.allow_joinleave = False
- org.save()
- team = self.create_team(organization=org)
- project = self.create_project(name="foo", organization=org, teams=[team])
- self.create_member(teams=[team], user=user, organization=org)
- self.login_as(user=user)
- url = reverse("sentry-api-0-organization-releases", kwargs={"organization_slug": org.slug})
- response = self.client.post(
- url,
- data={
- "version": "1.2.1",
- "projects": [project.slug],
- "refs": [{"repository": "not_a_repo", "commit": "a" * 40}],
- },
- )
- assert response.status_code == 400
- assert response.data == {"refs": ["Invalid repository names: not_a_repo"]}
- @region_silo_test(stable=True)
- class OrganizationReleaseCommitRangesTest(SetRefsTestCase):
- def setUp(self):
- super().setUp()
- self.url = reverse(
- "sentry-api-0-organization-releases", kwargs={"organization_slug": self.org.slug}
- )
- @patch("sentry.tasks.commits.fetch_commits")
- def test_simple(self, mock_fetch_commits):
- refs = [
- {
- "repository": "test/repo",
- "previousCommit": None,
- "commit": "previous-commit-id..current-commit-id",
- },
- {
- "repository": "test/repo",
- "previousCommit": "previous-commit-will-be-ignored",
- "commit": "previous-commit-id-2..current-commit-id-2",
- },
- {"repository": "test/repo", "commit": "previous-commit-id-3..current-commit-id-3"},
- ]
- response = self.client.post(
- self.url, data={"version": "1", "refs": refs, "projects": [self.project.slug]}
- )
- assert response.status_code == 201
- release = Release.objects.get(version="1", organization=self.org)
- commits = Commit.objects.all().order_by("id")
- self.assert_commit(commits[0], "current-commit-id")
- self.assert_commit(commits[1], "current-commit-id-2")
- self.assert_commit(commits[2], "current-commit-id-3")
- head_commits = ReleaseHeadCommit.objects.all()
- self.assert_head_commit(head_commits[0], "current-commit-id-3", release_id=release.id)
- refs_expected = [
- {
- "repository": "test/repo",
- "previousCommit": "previous-commit-id",
- "commit": "current-commit-id",
- },
- {
- "repository": "test/repo",
- "previousCommit": "previous-commit-id-2",
- "commit": "current-commit-id-2",
- },
- {
- "repository": "test/repo",
- "previousCommit": "previous-commit-id-3",
- "commit": "current-commit-id-3",
- },
- ]
- self.assert_fetch_commits(mock_fetch_commits, None, release.id, refs_expected)
- @patch("sentry.tasks.commits.fetch_commits")
- def test_head_commit(self, mock_fetch_commits):
- headCommits = [
- {
- "currentId": "current-commit-id",
- "previousId": "previous-commit-id",
- "repository": self.repo.name,
- },
- {
- "currentId": "current-commit-id-2",
- "previousId": "previous-commit-id-2",
- "repository": self.repo.name,
- },
- {
- "currentId": "current-commit-id-3",
- "previousId": "previous-commit-id-3",
- "repository": self.repo.name,
- },
- ]
- response = self.client.post(
- self.url,
- data={"version": "1", "headCommits": headCommits, "projects": [self.project.slug]},
- )
- assert response.status_code == 201
- release = Release.objects.get(version="1", organization=self.org)
- commits = Commit.objects.all().order_by("id")
- self.assert_commit(commits[0], "current-commit-id")
- self.assert_commit(commits[1], "current-commit-id-2")
- self.assert_commit(commits[2], "current-commit-id-3")
- head_commits = ReleaseHeadCommit.objects.all()
- self.assert_head_commit(head_commits[0], "current-commit-id-3", release_id=release.id)
- refs_expected = [
- {
- "repository": "test/repo",
- "previousCommit": "previous-commit-id",
- "commit": "current-commit-id",
- },
- {
- "repository": "test/repo",
- "previousCommit": "previous-commit-id-2",
- "commit": "current-commit-id-2",
- },
- {
- "repository": "test/repo",
- "previousCommit": "previous-commit-id-3",
- "commit": "current-commit-id-3",
- },
- ]
- self.assert_fetch_commits(mock_fetch_commits, None, release.id, refs_expected)
- @region_silo_test(stable=True)
- class OrganizationReleaseListEnvironmentsTest(APITestCase):
- def setUp(self):
- self.login_as(user=self.user)
- org = self.create_organization(owner=self.user)
- team = self.create_team(organization=org, members=[self.user])
- project1 = self.create_project(organization=org, teams=[team], name="foo")
- project2 = self.create_project(organization=org, teams=[team], name="bar")
- env1 = self.make_environment("prod", project1)
- env2 = self.make_environment("staging", project2)
- release1 = Release.objects.create(
- organization_id=org.id, version="1", date_added=datetime(2013, 8, 13, 3, 8, 24, 880386)
- )
- release1.add_project(project1)
- ReleaseProjectEnvironment.objects.create(
- project_id=project1.id, release_id=release1.id, environment_id=env1.id
- )
- release2 = Release.objects.create(
- organization_id=org.id, version="2", date_added=datetime(2013, 8, 14, 3, 8, 24, 880386)
- )
- release2.add_project(project2)
- ReleaseProjectEnvironment.objects.create(
- project_id=project2.id, release_id=release2.id, environment_id=env2.id
- )
- release3 = Release.objects.create(
- organization_id=org.id,
- version="3",
- date_added=datetime(2013, 8, 12, 3, 8, 24, 880386),
- date_released=datetime(2013, 8, 15, 3, 8, 24, 880386),
- )
- release3.add_project(project1)
- ReleaseProjectEnvironment.objects.create(
- project_id=project1.id, release_id=release3.id, environment_id=env2.id
- )
- release4 = Release.objects.create(organization_id=org.id, version="4")
- release4.add_project(project2)
- release5 = Release.objects.create(organization_id=org.id, version="5")
- release5.add_project(project1)
- release5.add_project(project2)
- ReleaseProjectEnvironment.objects.create(
- project_id=project1.id, release_id=release5.id, environment_id=env1.id
- )
- ReleaseProjectEnvironment.objects.create(
- project_id=project2.id, release_id=release5.id, environment_id=env2.id
- )
- self.project1 = project1
- self.project2 = project2
- self.release1 = release1
- self.release2 = release2
- self.release3 = release3
- self.release4 = release4
- self.release5 = release5
- self.env1 = env1
- self.env2 = env2
- self.org = org
- def make_environment(self, name, project):
- env = Environment.objects.create(organization_id=project.organization_id, name=name)
- env.add_project(project)
- return env
- def assert_releases(self, response, releases):
- assert response.status_code == 200, response.content
- assert len(response.data) == len(releases)
- response_versions = sorted(r["version"] for r in response.data)
- releases_versions = sorted(r.version for r in releases)
- assert response_versions == releases_versions
- def test_environments_filter(self):
- url = reverse(
- "sentry-api-0-organization-releases", kwargs={"organization_slug": self.org.slug}
- )
- response = self.client.get(url + "?environment=" + self.env1.name, format="json")
- self.assert_releases(response, [self.release1, self.release5])
- response = self.client.get(url + "?environment=" + self.env2.name, format="json")
- self.assert_releases(response, [self.release2, self.release3, self.release5])
- def test_empty_environment(self):
- url = reverse(
- "sentry-api-0-organization-releases", kwargs={"organization_slug": self.org.slug}
- )
- env = self.make_environment("", self.project2)
- ReleaseProjectEnvironment.objects.create(
- project_id=self.project2.id, release_id=self.release4.id, environment_id=env.id
- )
- response = self.client.get(url + "?environment=", format="json")
- self.assert_releases(response, [self.release4])
- def test_all_environments(self):
- url = reverse(
- "sentry-api-0-organization-releases", kwargs={"organization_slug": self.org.slug}
- )
- response = self.client.get(url, format="json")
- self.assert_releases(
- response, [self.release1, self.release2, self.release3, self.release4, self.release5]
- )
- def test_invalid_environment(self):
- url = reverse(
- "sentry-api-0-organization-releases", kwargs={"organization_slug": self.org.slug}
- )
- response = self.client.get(url + "?environment=" + "invalid_environment", format="json")
- assert response.status_code == 404
- def test_specify_project_ids(self):
- url = reverse(
- "sentry-api-0-organization-releases", kwargs={"organization_slug": self.org.slug}
- )
- response = self.client.get(url, format="json", data={"project": self.project1.id})
- self.assert_releases(response, [self.release1, self.release3, self.release5])
- response = self.client.get(url, format="json", data={"project": self.project2.id})
- self.assert_releases(response, [self.release2, self.release4, self.release5])
- response = self.client.get(
- url, format="json", data={"project": [self.project1.id, self.project2.id]}
- )
- self.assert_releases(
- response, [self.release1, self.release2, self.release3, self.release4, self.release5]
- )
- def test_date_range(self):
- url = reverse(
- "sentry-api-0-organization-releases", kwargs={"organization_slug": self.org.slug}
- )
- response = self.client.get(
- url,
- format="json",
- data={
- "start": (datetime.now() - timedelta(days=1)).isoformat() + "Z",
- "end": datetime.now().isoformat() + "Z",
- },
- )
- self.assert_releases(response, [self.release4, self.release5])
- def test_invalid_date_range(self):
- url = reverse(
- "sentry-api-0-organization-releases", kwargs={"organization_slug": self.org.slug}
- )
- response = self.client.get(url, format="json", data={"start": "null", "end": "null"})
- assert response.status_code == 400
- @region_silo_test(stable=True)
- class OrganizationReleaseCreateCommitPatch(ReleaseCommitPatchTest):
- @cached_property
- def url(self):
- return reverse(
- "sentry-api-0-organization-releases", kwargs={"organization_slug": self.org.slug}
- )
- def test_commits_with_patch_set(self):
- response = self.client.post(
- self.url,
- data={
- "version": "2d1ab93fe4bb42db80890f01f8358fc9f8fbff3b",
- "projects": [self.project.slug],
- "commits": [
- {
- "patch_set": [
- {"path": "hello.py", "type": "M"},
- {"path": "templates/hola.html", "type": "D"},
- ],
- "repository": "laurynsentry/helloworld",
- "author_email": "lauryndbrown@gmail.com",
- "timestamp": "2018-11-29T18:50:28+03:00",
- "author_name": "Lauryn Brown",
- "message": "made changes to hello.",
- "id": "2d1ab93fe4bb42db80890f01f8358fc9f8fbff3b",
- },
- {
- "patch_set": [
- {"path": "templates/hello.html", "type": "M"},
- {"path": "templates/goodbye.html", "type": "A"},
- ],
- "repository": "laurynsentry/helloworld",
- "author_email": "lauryndbrown@gmail.com",
- "timestamp": "2018-11-30T22:51:14+03:00",
- "author_name": "Lauryn Brown",
- "message": "Changed release",
- "id": "be2fe070f6d1b8a572b67defc87af2582f9b0d78",
- },
- ],
- },
- )
- assert response.status_code == 201, (response.status_code, response.content)
- assert response.data["version"]
- release = Release.objects.get(organization_id=self.org.id, version=response.data["version"])
- repo = Repository.objects.get(organization_id=self.org.id, name="laurynsentry/helloworld")
- assert repo.provider is None
- rc_list = list(
- ReleaseCommit.objects.filter(release=release)
- .select_related("commit", "commit__author")
- .order_by("order")
- )
- assert len(rc_list) == 2
- for rc in rc_list:
- assert rc.organization_id
- author = CommitAuthor.objects.get(
- organization_id=self.org.id, email="lauryndbrown@gmail.com"
- )
- assert author.name == "Lauryn Brown"
- commits = [rc.commit for rc in rc_list]
- commits.sort(key=lambda c: c.date_added)
- self.assert_commit(
- commit=commits[0],
- repo_id=repo.id,
- key="2d1ab93fe4bb42db80890f01f8358fc9f8fbff3b",
- author_id=author.id,
- message="made changes to hello.",
- )
- self.assert_commit(
- commit=commits[1],
- repo_id=repo.id,
- key="be2fe070f6d1b8a572b67defc87af2582f9b0d78",
- author_id=author.id,
- message="Changed release",
- )
- file_changes = CommitFileChange.objects.filter(organization_id=self.org.id).order_by(
- "filename"
- )
- self.assert_file_change(file_changes[0], "M", "hello.py", commits[0].id)
- self.assert_file_change(file_changes[1], "A", "templates/goodbye.html", commits[1].id)
- self.assert_file_change(file_changes[2], "M", "templates/hello.html", commits[1].id)
- self.assert_file_change(file_changes[3], "D", "templates/hola.html", commits[0].id)
- @region_silo_test(stable=True)
- class ReleaseSerializerWithProjectsTest(TestCase):
- def setUp(self):
- super().setUp()
- self.version = "1234567890"
- self.repo_name = "repo/name"
- self.repo2_name = "repo2/name"
- self.commits = [{"id": "a" * 40}, {"id": "b" * 40}]
- self.ref = "master"
- self.url = "https://example.com"
- self.dateReleased = "1000-10-10T06:06"
- self.headCommits = [
- {"currentId": "0" * 40, "repository": self.repo_name},
- {"currentId": "0" * 40, "repository": self.repo2_name},
- ]
- self.refs = [
- {"commit": "a" * 40, "previousCommit": "", "repository": self.repo_name},
- {"commit": "b" * 40, "previousCommit": "", "repository": self.repo2_name},
- ]
- self.projects = ["project_slug", "project2_slug"]
- def test_simple(self):
- serializer = ReleaseSerializerWithProjects(
- data={
- "version": self.version,
- "owner": self.user.username,
- "ref": self.ref,
- "url": self.url,
- "dateReleased": self.dateReleased,
- "commits": self.commits,
- "headCommits": self.headCommits,
- "refs": self.refs,
- "projects": self.projects,
- },
- context={"organization": self.organization},
- )
- assert serializer.is_valid(), serializer.errors
- assert sorted(serializer.fields.keys()) == sorted(
- [
- "version",
- "owner",
- "ref",
- "url",
- "dateReleased",
- "commits",
- "headCommits",
- "refs",
- "projects",
- "status",
- ]
- )
- result = serializer.validated_data
- assert result["version"] == self.version
- assert result["owner"]
- assert result["owner"].id == self.user.id
- assert result["owner"].username == self.user.username
- assert result["ref"] == self.ref
- assert result["url"] == self.url
- assert result["dateReleased"] == datetime(1000, 10, 10, 6, 6, tzinfo=pytz.UTC)
- assert result["commits"] == self.commits
- assert result["headCommits"] == self.headCommits
- assert result["refs"] == self.refs
- assert result["projects"] == self.projects
- def test_fields_not_required(self):
- serializer = ReleaseSerializerWithProjects(
- data={"version": self.version, "projects": self.projects},
- context={"organization": self.organization},
- )
- assert serializer.is_valid()
- result = serializer.validated_data
- assert result["version"] == self.version
- assert result["projects"] == self.projects
- def test_do_not_allow_null_commits(self):
- serializer = ReleaseSerializerWithProjects(
- data={"version": self.version, "projects": self.projects, "commits": None},
- context={"organization": self.organization},
- )
- assert not serializer.is_valid()
- def test_do_not_allow_null_head_commits(self):
- serializer = ReleaseSerializerWithProjects(
- data={"version": self.version, "projects": self.projects, "headCommits": None},
- context={"organization": self.organization},
- )
- assert not serializer.is_valid()
- def test_do_not_allow_null_refs(self):
- serializer = ReleaseSerializerWithProjects(
- data={"version": self.version, "projects": self.projects, "refs": None},
- context={"organization": self.organization},
- )
- assert not serializer.is_valid()
- def test_ref_limited_by_max_version_length(self):
- serializer = ReleaseSerializerWithProjects(
- data={
- "version": self.version,
- "projects": self.projects,
- "ref": "a" * MAX_VERSION_LENGTH,
- },
- context={"organization": self.organization},
- )
- assert serializer.is_valid()
- serializer = ReleaseSerializerWithProjects(
- data={
- "version": self.version,
- "projects": self.projects,
- "ref": "a" * (MAX_VERSION_LENGTH + 1),
- },
- context={"organization": self.organization},
- )
- assert not serializer.is_valid()
- def test_version_limited_by_max_version_length(self):
- serializer = ReleaseSerializerWithProjects(
- data={"version": "a" * MAX_VERSION_LENGTH, "projects": self.projects}
- )
- assert serializer.is_valid()
- serializer = ReleaseSerializerWithProjects(
- data={"version": "a" * (MAX_VERSION_LENGTH + 1), "projects": self.projects},
- context={"organization": self.organization},
- )
- assert not serializer.is_valid()
- def test_version_does_not_allow_whitespace(self):
- for char in BAD_RELEASE_CHARS:
- serializer = ReleaseSerializerWithProjects(
- data={"version": char, "projects": self.projects},
- context={"organization": self.organization},
- )
- assert not serializer.is_valid()
- def test_version_does_not_allow_current_dir_path(self):
- serializer = ReleaseSerializerWithProjects(data={"version": ".", "projects": self.projects})
- assert not serializer.is_valid()
- serializer = ReleaseSerializerWithProjects(
- data={"version": "..", "projects": self.projects},
- context={"organization": self.organization},
- )
- assert not serializer.is_valid()
- def test_version_does_not_allow_null_or_empty_value(self):
- serializer = ReleaseSerializerWithProjects(
- data={"version": None, "projects": self.projects},
- context={"organization": self.organization},
- )
- assert not serializer.is_valid()
- serializer = ReleaseSerializerWithProjects(
- data={"version": "", "projects": self.projects},
- context={"organization": self.organization},
- )
- assert not serializer.is_valid()
- def test_version_cannot_be_latest(self):
- serializer = ReleaseSerializerWithProjects(
- data={"version": "Latest", "projects": self.projects},
- context={"organization": self.organization},
- )
- assert not serializer.is_valid()
- @region_silo_test(stable=True)
- class ReleaseHeadCommitSerializerTest(unittest.TestCase):
- def setUp(self):
- super().setUp()
- self.repo_name = "repo/name"
- self.commit = "b" * 40
- self.commit_range = "{}..{}".format("a" * 40, "b" * 40)
- self.prev_commit = "a" * 40
- def test_simple(self):
- serializer = ReleaseHeadCommitSerializer(
- data={
- "commit": self.commit,
- "previousCommit": self.prev_commit,
- "repository": self.repo_name,
- }
- )
- assert serializer.is_valid()
- assert sorted(serializer.fields.keys()) == sorted(
- ["commit", "previousCommit", "repository"]
- )
- result = serializer.validated_data
- assert result["commit"] == self.commit
- assert result["previousCommit"] == self.prev_commit
- assert result["repository"] == self.repo_name
- def test_prev_commit_not_required(self):
- serializer = ReleaseHeadCommitSerializer(
- data={"commit": self.commit, "previousCommit": None, "repository": self.repo_name}
- )
- assert serializer.is_valid()
- def test_do_not_allow_null_or_empty_commit_or_repo(self):
- serializer = ReleaseHeadCommitSerializer(
- data={"commit": None, "previousCommit": self.prev_commit, "repository": self.repo_name}
- )
- assert not serializer.is_valid()
- serializer = ReleaseHeadCommitSerializer(
- data={"commit": "", "previousCommit": self.prev_commit, "repository": self.repo_name}
- )
- assert not serializer.is_valid()
- serializer = ReleaseHeadCommitSerializer(
- data={"commit": self.commit, "previousCommit": self.prev_commit, "repository": None}
- )
- assert not serializer.is_valid()
- serializer = ReleaseHeadCommitSerializer(
- data={"commit": self.commit, "previousCommit": self.prev_commit, "repository": ""}
- )
- assert not serializer.is_valid()
- def test_single_commit_limited_by_max_commit_length(self):
- serializer = ReleaseHeadCommitSerializer(
- data={"commit": "b" * MAX_COMMIT_LENGTH, "repository": self.repo_name}
- )
- assert serializer.is_valid()
- serializer = ReleaseHeadCommitSerializer(
- data={
- "commit": self.commit,
- "previousCommit": "a" * MAX_COMMIT_LENGTH,
- "repository": self.repo_name,
- }
- )
- assert serializer.is_valid()
- serializer = ReleaseHeadCommitSerializer(
- data={"commit": "b" * (MAX_COMMIT_LENGTH + 1), "repository": self.repo_name}
- )
- assert not serializer.is_valid()
- serializer = ReleaseHeadCommitSerializer(
- data={
- "commit": self.commit,
- "previousCommit": "a" * (MAX_COMMIT_LENGTH + 1),
- "repository": self.repo_name,
- }
- )
- assert not serializer.is_valid()
- def test_commit_range_does_not_allow_empty_commits(self):
- serializer = ReleaseHeadCommitSerializer(
- data={
- "commit": "{}..{}".format("", "b" * MAX_COMMIT_LENGTH),
- "repository": self.repo_name,
- }
- )
- assert not serializer.is_valid()
- serializer = ReleaseHeadCommitSerializer(
- data={
- "commit": "{}..{}".format("a" * MAX_COMMIT_LENGTH, ""),
- "repository": self.repo_name,
- }
- )
- assert not serializer.is_valid()
- def test_commit_range_limited_by_max_commit_length(self):
- serializer = ReleaseHeadCommitSerializer(
- data={
- "commit": "{}..{}".format("a" * MAX_COMMIT_LENGTH, "b" * MAX_COMMIT_LENGTH),
- "repository": self.repo_name,
- }
- )
- assert serializer.is_valid()
- serializer = ReleaseHeadCommitSerializer(
- data={
- "commit": "{}..{}".format("a" * (MAX_COMMIT_LENGTH + 1), "b" * MAX_COMMIT_LENGTH),
- "repository": self.repo_name,
- }
- )
- assert not serializer.is_valid()
- serializer = ReleaseHeadCommitSerializer(
- data={
- "commit": "{}..{}".format("a" * MAX_COMMIT_LENGTH, "b" * (MAX_COMMIT_LENGTH + 1)),
- "repository": self.repo_name,
- }
- )
- assert not serializer.is_valid()
|