@@ -2072,3 +2072,226 @@ def test_invalid_step_defaults_to_1(self):
20722072 response = self .client .get (reverse ("manage_composer" ) + "?step=abc" )
20732073 self .assertEqual (response .status_code , 200 )
20742074 self .assertContains (response , "Select a Sponsor" )
2075+
2076+
2077+ class DashboardExpiringSoonTests (SponsorManageTestBase ):
2078+ """Test dashboard expiring/expired sponsorship sections."""
2079+
2080+ @classmethod
2081+ def setUpTestData (cls ):
2082+ super ().setUpTestData ()
2083+ cls .sponsor = Sponsor .objects .create (name = "Expiring Corp" )
2084+
2085+ def setUp (self ):
2086+ super ().setUp ()
2087+ self .client .login (username = "staff" , password = "pass" )
2088+
2089+ def test_expiring_soon_shown_on_dashboard (self ):
2090+ """Finalized sponsorship ending within 90 days appears in Expiring Soon."""
2091+ today = timezone .now ().date ()
2092+ Sponsorship .objects .create (
2093+ sponsor = self .sponsor ,
2094+ submited_by = self .staff_user ,
2095+ package = self .package ,
2096+ sponsorship_fee = 100000 ,
2097+ year = self .year ,
2098+ status = Sponsorship .FINALIZED ,
2099+ start_date = today - datetime .timedelta (days = 300 ),
2100+ end_date = today + datetime .timedelta (days = 30 ),
2101+ )
2102+ response = self .client .get (reverse ("manage_dashboard" ) + f"?year={ self .year } " )
2103+ self .assertEqual (response .status_code , 200 )
2104+ self .assertContains (response , "Expiring Soon" )
2105+ self .assertContains (response , "Expiring Corp" )
2106+
2107+ def test_expiring_far_future_not_shown (self ):
2108+ """Finalized sponsorship ending more than 90 days out is not in Expiring Soon."""
2109+ today = timezone .now ().date ()
2110+ Sponsorship .objects .create (
2111+ sponsor = self .sponsor ,
2112+ submited_by = self .staff_user ,
2113+ package = self .package ,
2114+ sponsorship_fee = 100000 ,
2115+ year = self .year ,
2116+ status = Sponsorship .FINALIZED ,
2117+ start_date = today - datetime .timedelta (days = 100 ),
2118+ end_date = today + datetime .timedelta (days = 200 ),
2119+ )
2120+ response = self .client .get (reverse ("manage_dashboard" ) + f"?year={ self .year } " )
2121+ self .assertNotContains (response , "Expiring Soon" )
2122+
2123+ def test_recently_expired_shown_on_dashboard (self ):
2124+ """Finalized sponsorship with past end_date appears in Recently Expired."""
2125+ today = timezone .now ().date ()
2126+ Sponsorship .objects .create (
2127+ sponsor = self .sponsor ,
2128+ submited_by = self .staff_user ,
2129+ package = self .package ,
2130+ sponsorship_fee = 100000 ,
2131+ year = self .year ,
2132+ status = Sponsorship .FINALIZED ,
2133+ start_date = today - datetime .timedelta (days = 400 ),
2134+ end_date = today - datetime .timedelta (days = 10 ),
2135+ )
2136+ response = self .client .get (reverse ("manage_dashboard" ) + f"?year={ self .year } " )
2137+ self .assertEqual (response .status_code , 200 )
2138+ self .assertContains (response , "Recently Expired" )
2139+ self .assertContains (response , "Expiring Corp" )
2140+
2141+ def test_overlapped_expired_not_shown (self ):
2142+ """Expired sponsorship with overlapped_by set is excluded from Recently Expired."""
2143+ today = timezone .now ().date ()
2144+ renewal = Sponsorship .objects .create (
2145+ sponsor = self .sponsor ,
2146+ submited_by = self .staff_user ,
2147+ package = self .package ,
2148+ sponsorship_fee = 100000 ,
2149+ year = self .year ,
2150+ status = Sponsorship .FINALIZED ,
2151+ start_date = today - datetime .timedelta (days = 30 ),
2152+ end_date = today + datetime .timedelta (days = 335 ),
2153+ )
2154+ Sponsorship .objects .create (
2155+ sponsor = self .sponsor ,
2156+ submited_by = self .staff_user ,
2157+ package = self .package ,
2158+ sponsorship_fee = 100000 ,
2159+ year = self .year ,
2160+ status = Sponsorship .FINALIZED ,
2161+ start_date = today - datetime .timedelta (days = 400 ),
2162+ end_date = today - datetime .timedelta (days = 10 ),
2163+ overlapped_by = renewal ,
2164+ )
2165+ response = self .client .get (reverse ("manage_dashboard" ) + f"?year={ self .year } " )
2166+ self .assertNotContains (response , "Recently Expired" )
2167+
2168+ def test_applied_sponsorship_not_in_expiring (self ):
2169+ """Only finalized sponsorships appear in expiring sections."""
2170+ today = timezone .now ().date ()
2171+ Sponsorship .objects .create (
2172+ sponsor = self .sponsor ,
2173+ submited_by = self .staff_user ,
2174+ package = self .package ,
2175+ sponsorship_fee = 100000 ,
2176+ year = self .year ,
2177+ status = Sponsorship .APPLIED ,
2178+ start_date = today - datetime .timedelta (days = 300 ),
2179+ end_date = today + datetime .timedelta (days = 10 ),
2180+ )
2181+ response = self .client .get (reverse ("manage_dashboard" ) + f"?year={ self .year } " )
2182+ self .assertNotContains (response , "Expiring Soon" )
2183+
2184+ def test_expiring_shown_cross_year (self ):
2185+ """Expiring sponsorships from a prior year show on the current year dashboard."""
2186+ today = timezone .now ().date ()
2187+ prior_year = self .year - 1
2188+ Sponsorship .objects .create (
2189+ sponsor = self .sponsor ,
2190+ submited_by = self .staff_user ,
2191+ package = self .package ,
2192+ sponsorship_fee = 100000 ,
2193+ year = prior_year ,
2194+ status = Sponsorship .FINALIZED ,
2195+ start_date = today - datetime .timedelta (days = 300 ),
2196+ end_date = today + datetime .timedelta (days = 30 ),
2197+ )
2198+ response = self .client .get (reverse ("manage_dashboard" ) + f"?year={ self .year } " )
2199+ self .assertContains (response , "Expiring Soon" )
2200+ self .assertContains (response , "Expiring Corp" )
2201+
2202+
2203+ class SponsorshipDetailRenewalTests (SponsorshipReviewTestBase ):
2204+ """Test renewal-related features on sponsorship detail view."""
2205+
2206+ def test_create_renewal_button_shown_for_finalized (self ):
2207+ """Finalized sponsorships show the + Renewal button."""
2208+ self .sponsorship .status = Sponsorship .FINALIZED
2209+ self .sponsorship .save (update_fields = ["status" ])
2210+ response = self .client .get (reverse ("manage_sponsorship_detail" , args = [self .sponsorship .pk ]))
2211+ self .assertContains (response , "+ Renewal" )
2212+ self .assertContains (response , "renewal=1" )
2213+
2214+ def test_create_renewal_button_hidden_for_applied (self ):
2215+ """Applied sponsorships do not show the + Renewal button."""
2216+ response = self .client .get (reverse ("manage_sponsorship_detail" , args = [self .sponsorship .pk ]))
2217+ self .assertNotContains (response , "+ Renewal" )
2218+
2219+ def test_expiring_soon_tag_shown (self ):
2220+ """Finalized sponsorship with end_date within 90 days shows Expiring Soon tag."""
2221+ today = timezone .now ().date ()
2222+ self .sponsorship .status = Sponsorship .FINALIZED
2223+ self .sponsorship .start_date = today - datetime .timedelta (days = 300 )
2224+ self .sponsorship .end_date = today + datetime .timedelta (days = 30 )
2225+ self .sponsorship .save ()
2226+ response = self .client .get (reverse ("manage_sponsorship_detail" , args = [self .sponsorship .pk ]))
2227+ self .assertContains (response , "Expiring Soon" )
2228+
2229+ def test_expired_tag_shown (self ):
2230+ """Finalized sponsorship with end_date in past shows Expired tag."""
2231+ today = timezone .now ().date ()
2232+ self .sponsorship .status = Sponsorship .FINALIZED
2233+ self .sponsorship .start_date = today - datetime .timedelta (days = 400 )
2234+ self .sponsorship .end_date = today - datetime .timedelta (days = 10 )
2235+ self .sponsorship .save ()
2236+ response = self .client .get (reverse ("manage_sponsorship_detail" , args = [self .sponsorship .pk ]))
2237+ self .assertContains (response , "Expired" )
2238+
2239+ def test_no_expiry_tag_when_not_near_end (self ):
2240+ """Finalized sponsorship with distant end_date shows no expiry tags."""
2241+ today = timezone .now ().date ()
2242+ self .sponsorship .status = Sponsorship .FINALIZED
2243+ self .sponsorship .start_date = today - datetime .timedelta (days = 100 )
2244+ self .sponsorship .end_date = today + datetime .timedelta (days = 200 )
2245+ self .sponsorship .save ()
2246+ response = self .client .get (reverse ("manage_sponsorship_detail" , args = [self .sponsorship .pk ]))
2247+ self .assertNotContains (response , "Expiring Soon" )
2248+ self .assertNotContains (response , ">Expired<" )
2249+
2250+
2251+ class SponsorshipListExpiryTagTests (SponsorshipReviewTestBase ):
2252+ """Test expiry tags on sponsorship list view."""
2253+
2254+ def test_expired_tag_shown_in_list (self ):
2255+ """Expired finalized sponsorship shows 'Expired' tag in list."""
2256+ today = timezone .now ().date ()
2257+ self .sponsorship .status = Sponsorship .FINALIZED
2258+ self .sponsorship .start_date = today - datetime .timedelta (days = 400 )
2259+ self .sponsorship .end_date = today - datetime .timedelta (days = 10 )
2260+ self .sponsorship .save ()
2261+ response = self .client .get (reverse ("manage_sponsorships" ) + "?status=finalized" )
2262+ self .assertContains (response , "Expired" )
2263+
2264+ def test_days_left_tag_shown_in_list (self ):
2265+ """Expiring finalized sponsorship shows days-left tag in list."""
2266+ today = timezone .now ().date ()
2267+ self .sponsorship .status = Sponsorship .FINALIZED
2268+ self .sponsorship .start_date = today - datetime .timedelta (days = 300 )
2269+ self .sponsorship .end_date = today + datetime .timedelta (days = 20 )
2270+ self .sponsorship .save ()
2271+ response = self .client .get (reverse ("manage_sponsorships" ) + "?status=finalized" )
2272+ self .assertContains (response , "d left" )
2273+
2274+
2275+ class ComposerRenewalPreFillTests (SponsorManageTestBase ):
2276+ """Test that composer pre-fills renewal flag from query param."""
2277+
2278+ @classmethod
2279+ def setUpTestData (cls ):
2280+ super ().setUpTestData ()
2281+ cls .sponsor = Sponsor .objects .create (name = "Renewing Corp" )
2282+
2283+ def setUp (self ):
2284+ super ().setUp ()
2285+ self .client .login (username = "staff" , password = "pass" )
2286+
2287+ def test_renewal_flag_stored_in_session (self ):
2288+ """Starting composer with renewal=1 stores renewal in session."""
2289+ self .client .get (reverse ("manage_composer" ) + f"?new=1&sponsor_id={ self .sponsor .pk } &renewal=1" )
2290+ session_data = self .client .session .get ("composer" , {})
2291+ self .assertTrue (session_data .get ("renewal" ))
2292+
2293+ def test_renewal_flag_not_stored_without_param (self ):
2294+ """Starting composer without renewal param does not store renewal."""
2295+ self .client .get (reverse ("manage_composer" ) + f"?new=1&sponsor_id={ self .sponsor .pk } " )
2296+ session_data = self .client .session .get ("composer" , {})
2297+ self .assertNotIn ("renewal" , session_data )
0 commit comments