Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / tests / projects.py @ e98239db

History | View | Annotate | Download (30.5 kB)

1
# Copyright 2011, 2012, 2013 GRNET S.A. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or
4
# without modification, are permitted provided that the following
5
# conditions are met:
6
#
7
#   1. Redistributions of source code must retain the above
8
#      copyright notice, this list of conditions and the following
9
#      disclaimer.
10
#
11
#   2. Redistributions in binary form must reproduce the above
12
#      copyright notice, this list of conditions and the following
13
#      disclaimer in the documentation and/or other materials
14
#      provided with the distribution.
15
#
16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
# POSSIBILITY OF SUCH DAMAGE.
28
#
29
# The views and conclusions contained in the software and
30
# documentation are those of the authors and should not be
31
# interpreted as representing official policies, either expressed
32
# or implied, of GRNET S.A.
33

    
34
from astakos.im.tests.common import *
35
from snf_django.utils.testing import assertGreater, assertIn, assertRaises
36

    
37

    
38
class ProjectAPITest(TestCase):
39

    
40
    def setUp(self):
41
        self.client = Client()
42
        component1 = Component.objects.create(name="comp1")
43
        register.add_service(component1, "service1", "type1", [])
44
        # custom service resources
45
        resource11 = {"name": "service1.resource11",
46
                      "desc": "resource11 desc",
47
                      "service_type": "type1",
48
                      "service_origin": "service1",
49
                      "allow_in_projects": True}
50
        r, _ = register.add_resource(resource11)
51
        register.update_resource(r, 100)
52
        resource12 = {"name": "service1.resource12",
53
                      "desc": "resource11 desc",
54
                      "service_type": "type1",
55
                      "service_origin": "service1",
56
                      "unit": "bytes"}
57
        r, _ = register.add_resource(resource12)
58
        register.update_resource(r, 1024)
59

    
60
        # create user
61
        self.user1 = get_local_user("test@grnet.gr")
62
        quotas.qh_sync_user(self.user1)
63
        self.user2 = get_local_user("test2@grnet.gr")
64
        self.user2.uuid = "uuid2"
65
        self.user2.save()
66
        quotas.qh_sync_user(self.user2)
67
        self.user3 = get_local_user("test3@grnet.gr")
68
        quotas.qh_sync_user(self.user3)
69

    
70
        astakos = Component.objects.create(name="astakos")
71
        register.add_service(astakos, "astakos_account", "account", [])
72
        # create another service
73
        pending_app = {"name": "astakos.pending_app",
74
                       "desc": "pend app desc",
75
                       "service_type": "account",
76
                       "service_origin": "astakos_account",
77
                       "allow_in_projects": False}
78
        r, _ = register.add_resource(pending_app)
79
        register.update_resource(r, 3)
80

    
81
    def create(self, app, headers):
82
        dump = json.dumps(app)
83
        r = self.client.post(reverse("api_projects"), dump,
84
                             content_type="application/json", **headers)
85
        body = json.loads(r.content)
86
        return r.status_code, body
87

    
88
    def modify(self, app, project_id, headers):
89
        dump = json.dumps(app)
90
        kwargs = {"project_id": project_id}
91
        r = self.client.post(reverse("api_project", kwargs=kwargs), dump,
92
                             content_type="application/json", **headers)
93
        body = json.loads(r.content)
94
        return r.status_code, body
95

    
96
    def project_action(self, project_id, action, headers):
97
        action = json.dumps({action: "reason"})
98
        r = self.client.post(reverse("api_project_action",
99
                                     kwargs={"project_id": project_id}),
100
                             action, content_type="application/json",
101
                             **headers)
102
        return r.status_code
103

    
104
    def app_action(self, app_id, action, headers):
105
        action = json.dumps({action: "reason"})
106
        r = self.client.post(reverse("api_application_action",
107
                                     kwargs={"app_id": app_id}),
108
                             action, content_type="application/json",
109
                             **headers)
110
        return r.status_code
111

    
112
    def memb_action(self, memb_id, action, headers):
113
        action = json.dumps({action: "reason"})
114
        r = self.client.post(reverse("api_membership_action",
115
                                     kwargs={"memb_id": memb_id}), action,
116
                             content_type="application/json", **headers)
117
        return r.status_code
118

    
119
    def join(self, project_id, headers):
120
        action = {"join": {"project": project_id}}
121
        req = json.dumps(action)
122
        r = self.client.post(reverse("api_memberships"), req,
123
                             content_type="application/json", **headers)
124
        body = json.loads(r.content)
125
        return r.status_code, body
126

    
127
    def enroll(self, project_id, user, headers):
128
        action = {
129
            "enroll": {
130
                "project": project_id,
131
                "user": user.email,
132
            }
133
        }
134
        req = json.dumps(action)
135
        r = self.client.post(reverse("api_memberships"), req,
136
                             content_type="application/json", **headers)
137
        body = json.loads(r.content)
138
        return r.status_code, body
139

    
140
    @im_settings(PROJECT_ADMINS=["uuid2"])
141
    def test_projects(self):
142
        client = self.client
143
        h_owner = {"HTTP_X_AUTH_TOKEN": self.user1.auth_token}
144
        h_admin = {"HTTP_X_AUTH_TOKEN": self.user2.auth_token}
145
        h_plain = {"HTTP_X_AUTH_TOKEN": self.user3.auth_token}
146
        r = client.get(reverse("api_project", kwargs={"project_id": 1}))
147
        self.assertEqual(r.status_code, 401)
148

    
149
        r = client.get(reverse("api_project", kwargs={"project_id": 1}),
150
                       **h_owner)
151
        self.assertEqual(r.status_code, 404)
152
        r = client.get(reverse("api_application", kwargs={"app_id": 1}),
153
                       **h_owner)
154
        self.assertEqual(r.status_code, 404)
155
        r = client.get(reverse("api_membership", kwargs={"memb_id": 1}),
156
                       **h_owner)
157
        self.assertEqual(r.status_code, 404)
158

    
159
        status = self.memb_action(1, "accept", h_admin)
160
        self.assertEqual(status, 404)
161

    
162
        app1 = {"name": "test.pr",
163
                "end_date": "2013-5-5T20:20:20Z",
164
                "join_policy": "auto",
165
                "max_members": 5,
166
                "resources": {"service1.resource11": {
167
                    "member_capacity": 512}}
168
                }
169

    
170
        status, body = self.modify(app1, 1, h_owner)
171
        self.assertEqual(status, 404)
172

    
173
        # Create
174
        status, body = self.create(app1, h_owner)
175
        self.assertEqual(status, 201)
176
        project_id = body["id"]
177
        app_id = body["application"]
178

    
179
        # Get project
180
        r = client.get(reverse("api_project",
181
                               kwargs={"project_id": project_id}),
182
                       **h_owner)
183
        self.assertEqual(r.status_code, 200)
184
        body = json.loads(r.content)
185
        self.assertEqual(body["id"], project_id)
186
        self.assertEqual(body["application"], app_id)
187
        self.assertEqual(body["state"], "pending")
188
        self.assertEqual(body["owner"], self.user1.uuid)
189

    
190
        # Approve forbidden
191
        status = self.app_action(app_id, "approve", h_owner)
192
        self.assertEqual(status, 403)
193

    
194
        # Create another with the same name
195
        status, body = self.create(app1, h_owner)
196
        self.assertEqual(status, 201)
197
        project2_id = body["id"]
198
        project2_app_id = body["application"]
199

    
200
        # Create yet another, with different name
201
        app_p3 = copy.deepcopy(app1)
202
        app_p3["name"] = "new.pr"
203
        status, body = self.create(app_p3, h_owner)
204
        self.assertEqual(status, 201)
205
        project3_app_id = body["application"]
206

    
207
        # No more pending allowed
208
        status, body = self.create(app_p3, h_owner)
209
        self.assertEqual(status, 409)
210

    
211
        # Cancel
212
        status = self.app_action(project3_app_id, "cancel", h_owner)
213
        self.assertEqual(status, 200)
214

    
215
        # Modify
216
        app2 = {"name": "test.pr",
217
                "start_date": "2013-5-5T20:20:20Z",
218
                "end_date": "2013-7-5T20:20:20Z",
219
                "join_policy": "moderated",
220
                "leave_policy": "auto",
221
                "max_members": 3,
222
                "resources": {"service1.resource11": {
223
                    "member_capacity": 1024}}
224
                }
225

    
226
        status, body = self.modify(app2, project_id, h_owner)
227
        self.assertEqual(status, 201)
228
        self.assertEqual(project_id, body["id"])
229
        app2_id = body["application"]
230
        assertGreater(app2_id, app_id)
231

    
232
        # Dismiss failed
233
        status = self.app_action(app2_id, "dismiss", h_owner)
234
        self.assertEqual(status, 409)
235

    
236
        # Deny
237
        status = self.app_action(app2_id, "deny", h_admin)
238
        self.assertEqual(status, 200)
239

    
240
        r = client.get(reverse("api_application", kwargs={"app_id": app2_id}),
241
                       **h_owner)
242
        body = json.loads(r.content)
243
        self.assertEqual(body["state"], "denied")
244

    
245
        # Dismiss
246
        status = self.app_action(app2_id, "dismiss", h_owner)
247
        self.assertEqual(status, 200)
248

    
249
        # Resubmit
250
        status, body = self.modify(app2, project_id, h_owner)
251
        self.assertEqual(status, 201)
252
        app3_id = body["application"]
253

    
254
        # Approve
255
        status = self.app_action(app3_id, "approve", h_admin)
256
        self.assertEqual(status, 200)
257

    
258
        # Get related apps
259
        req = {"body": json.dumps({"project": project_id})}
260
        r = client.get(reverse("api_applications"), req, **h_owner)
261
        self.assertEqual(r.status_code, 200)
262
        body = json.loads(r.content)
263
        self.assertEqual(len(body), 3)
264

    
265
        # Get apps
266
        r = client.get(reverse("api_applications"), **h_owner)
267
        self.assertEqual(r.status_code, 200)
268
        body = json.loads(r.content)
269
        self.assertEqual(len(body), 5)
270

    
271
        # Enroll
272
        status, body = self.enroll(project_id, self.user3, h_owner)
273
        self.assertEqual(status, 200)
274
        m_plain_id = body["id"]
275

    
276
        # Join
277
        status, body = self.join(project_id, h_owner)
278
        self.assertEqual(status, 200)
279
        memb_id = body["id"]
280

    
281
        # Check memberships
282
        r = client.get(reverse("api_memberships"), **h_plain)
283
        body = json.loads(r.content)
284
        self.assertEqual(len(body), 1)
285
        m = body[0]
286
        self.assertEqual(m["user"], self.user3.uuid)
287
        self.assertEqual(m["state"], "accepted")
288

    
289
        r = client.get(reverse("api_memberships"), **h_owner)
290
        body = json.loads(r.content)
291
        self.assertEqual(len(body), 2)
292

    
293
        # Check membership
294
        r = client.get(reverse("api_membership", kwargs={"memb_id": memb_id}),
295
                       **h_admin)
296
        m = json.loads(r.content)
297
        self.assertEqual(m["user"], self.user1.uuid)
298
        self.assertEqual(m["state"], "requested")
299
        self.assertEqual(sorted(m["allowed_actions"]),
300
                         ["accept", "cancel", "reject"])
301

    
302
        r = client.get(reverse("api_membership", kwargs={"memb_id": memb_id}),
303
                       **h_plain)
304
        self.assertEqual(r.status_code, 403)
305

    
306
        status = self.memb_action(memb_id, "leave", h_admin)
307
        self.assertEqual(status, 409)
308

    
309
        status = self.memb_action(memb_id, "cancel", h_owner)
310
        self.assertEqual(status, 200)
311

    
312
        status, body = self.join(project_id, h_owner)
313
        self.assertEqual(status, 200)
314
        self.assertEqual(memb_id, body["id"])
315

    
316
        status = self.memb_action(memb_id, "reject", h_owner)
317
        self.assertEqual(status, 200)
318

    
319
        status, body = self.join(project_id, h_owner)
320
        self.assertEqual(status, 200)
321
        self.assertEqual(memb_id, body["id"])
322

    
323
        status = self.memb_action(memb_id, "accept", h_owner)
324
        self.assertEqual(status, 200)
325

    
326
        # Enroll fails, already in
327
        status, body = self.enroll(project_id, self.user1, h_owner)
328
        self.assertEqual(status, 409)
329

    
330
        # Remove member
331
        status = self.memb_action(memb_id, "remove", h_owner)
332
        self.assertEqual(status, 200)
333

    
334
        # Enroll a removed member
335
        status, body = self.enroll(project_id, self.user1, h_owner)
336
        self.assertEqual(status, 200)
337

    
338
        # Remove member
339
        status = self.memb_action(memb_id, "remove", h_owner)
340
        self.assertEqual(status, 200)
341

    
342
        # Re-join
343
        status, body = self.join(project_id, h_owner)
344
        self.assertEqual(status, 200)
345
        self.assertEqual(memb_id, body["id"])
346

    
347
        # Enroll a requested member
348
        status, body = self.enroll(project_id, self.user1, h_owner)
349
        self.assertEqual(status, 200)
350

    
351
        # Enroll fails, already in
352
        status, body = self.enroll(project_id, self.user1, h_owner)
353
        self.assertEqual(status, 409)
354

    
355
        # Get projects
356
        ## Simple user mode
357
        r = client.get(reverse("api_projects"), **h_plain)
358
        body = json.loads(r.content)
359
        self.assertEqual(len(body), 1)
360
        p = body[0]
361
        with assertRaises(KeyError):
362
            p["pending_application"]
363

    
364
        ## Owner mode
365
        filters = {"filter": {"state": ["active", "cancelled"]}}
366
        req = {"body": json.dumps(filters)}
367
        r = client.get(reverse("api_projects"), req, **h_owner)
368
        body = json.loads(r.content)
369
        self.assertEqual(len(body), 2)
370
        assertIn("pending_application", body[0])
371

    
372
        filters = {"filter": {"state": "pending"}}
373
        req = {"body": json.dumps(filters)}
374
        r = client.get(reverse("api_projects"), req, **h_owner)
375
        body = json.loads(r.content)
376
        self.assertEqual(len(body), 1)
377
        self.assertEqual(body[0]["id"], project2_id)
378

    
379
        filters = {"filter": {"name": "test.pr"}}
380
        req = {"body": json.dumps(filters)}
381
        r = client.get(reverse("api_projects"), req, **h_owner)
382
        body = json.loads(r.content)
383
        self.assertEqual(len(body), 2)
384

    
385
        # Leave failed
386
        status = self.memb_action(m_plain_id, "leave", h_owner)
387
        self.assertEqual(status, 403)
388

    
389
        # Leave
390
        status = self.memb_action(m_plain_id, "leave", h_plain)
391
        self.assertEqual(status, 200)
392

    
393
        # Suspend failed
394
        status = self.project_action(project_id, "suspend", h_owner)
395
        self.assertEqual(status, 403)
396

    
397
        # Unsuspend failed
398
        status = self.project_action(project_id, "unsuspend", h_admin)
399
        self.assertEqual(status, 409)
400

    
401
        # Suspend
402
        status = self.project_action(project_id, "suspend", h_admin)
403
        self.assertEqual(status, 200)
404

    
405
        # Cannot view project
406
        r = client.get(reverse("api_project",
407
                               kwargs={"project_id": project_id}), **h_plain)
408
        self.assertEqual(r.status_code, 403)
409

    
410
        # Unsuspend
411
        status = self.project_action(project_id, "unsuspend", h_admin)
412
        self.assertEqual(status, 200)
413

    
414
        # Cannot approve, project with same name exists
415
        status = self.app_action(project2_app_id, "approve", h_admin)
416
        self.assertEqual(status, 409)
417

    
418
        # Terminate
419
        status = self.project_action(project_id, "terminate", h_admin)
420
        self.assertEqual(status, 200)
421

    
422
        # Join failed
423
        status, _ = self.join(project_id, h_admin)
424
        self.assertEqual(status, 409)
425

    
426
        # Can approve now
427
        status = self.app_action(project2_app_id, "approve", h_admin)
428
        self.assertEqual(status, 200)
429

    
430
        # Join new project
431
        status, body = self.join(project2_id, h_plain)
432
        self.assertEqual(status, 200)
433
        m_project2 = body["id"]
434

    
435
        # Get memberships of project
436
        body = {"body": json.dumps({"project": project2_id})}
437
        r = client.get(reverse("api_memberships"), body, **h_owner)
438
        body = json.loads(r.content)
439
        self.assertEqual(len(body), 1)
440
        self.assertEqual(body[0]["id"], m_project2)
441

    
442
        # Remove member
443
        status = self.memb_action(m_project2, "remove", h_owner)
444
        self.assertEqual(status, 200)
445

    
446
        # Reinstate failed
447
        status = self.project_action(project_id, "reinstate", h_admin)
448
        self.assertEqual(status, 409)
449

    
450
        # Rename
451
        app2_renamed = copy.deepcopy(app2)
452
        app2_renamed["name"] = "new.name"
453
        status, body = self.modify(app2_renamed, project_id, h_owner)
454
        self.assertEqual(status, 201)
455
        app2_renamed_id = body["application"]
456

    
457
        # Get project
458
        r = client.get(reverse("api_project",
459
                               kwargs={"project_id": project_id}), **h_owner)
460
        body = json.loads(r.content)
461
        self.assertEqual(body["application"], app3_id)
462
        self.assertEqual(body["pending_application"], app2_renamed_id)
463
        self.assertEqual(body["state"], "terminated")
464
        assertIn("deactivation_date", body)
465

    
466
        # Get application
467
        r = client.get(reverse("api_application",
468
                               kwargs={"app_id": app2_renamed_id}), **h_plain)
469
        self.assertEqual(r.status_code, 403)
470

    
471
        r = client.get(reverse("api_application",
472
                               kwargs={"app_id": app2_renamed_id}), **h_owner)
473
        self.assertEqual(r.status_code, 200)
474
        body = json.loads(r.content)
475
        self.assertEqual(body["state"], "pending")
476
        self.assertEqual(body["name"], "new.name")
477

    
478
        # Approve (automatically reinstates)
479
        action = json.dumps({"approve": ""})
480
        r = client.post(reverse("api_application_action",
481
                                kwargs={"app_id": app2_renamed_id}),
482
                        action, content_type="application/json", **h_admin)
483
        self.assertEqual(r.status_code, 200)
484

    
485
        # Bad requests
486
        r = client.head(reverse("api_projects"), **h_admin)
487
        self.assertEqual(r.status_code, 400)
488

    
489
        r = client.head(reverse("api_project",
490
                                kwargs={"project_id": 1}), **h_admin)
491
        self.assertEqual(r.status_code, 400)
492

    
493
        r = client.head(reverse("api_applications"), **h_admin)
494
        self.assertEqual(r.status_code, 400)
495

    
496
        r = client.head(reverse("api_memberships"), **h_admin)
497
        self.assertEqual(r.status_code, 400)
498

    
499
        status = self.project_action(1, "nonex", h_owner)
500
        self.assertEqual(status, 400)
501

    
502
        action = json.dumps({"suspend": "", "unsuspend": ""})
503
        r = client.post(reverse("api_project_action",
504
                                kwargs={"project_id": 1}),
505
                        action, content_type="application/json", **h_owner)
506
        self.assertEqual(r.status_code, 400)
507

    
508
        ap = {"owner": "nonex",
509
              "join_policy": "nonex",
510
              "leave_policy": "nonex",
511
              "start_date": "nonex",
512
              "homepage": {},
513
              "max_members": -3,
514
              "resources": [],
515
              }
516

    
517
        status, body = self.create(ap, h_owner)
518
        self.assertEqual(status, 400)
519
        self.assertEqual(body["badRequest"]["message"], "User does not exist.")
520

    
521
        ap["owner"] = self.user1.uuid
522
        status, body = self.create(ap, h_owner)
523
        self.assertEqual(status, 400)
524

    
525
        ap["name"] = "some.name"
526
        status, body = self.create(ap, h_owner)
527
        self.assertEqual(status, 400)
528

    
529
        ap["join_policy"] = "auto"
530
        status, body = self.create(ap, h_owner)
531
        self.assertEqual(status, 400)
532

    
533
        ap["leave_policy"] = "closed"
534
        status, body = self.create(ap, h_owner)
535
        self.assertEqual(status, 400)
536

    
537
        ap["start_date"] = "2013-01-01T0:0Z"
538
        status, body = self.create(ap, h_owner)
539
        self.assertEqual(status, 400)
540

    
541
        ap["end_date"] = "2014-01-01T0:0Z"
542
        status, body = self.create(ap, h_owner)
543
        self.assertEqual(status, 400)
544

    
545
        ap["max_members"] = 0
546
        status, body = self.create(ap, h_owner)
547
        self.assertEqual(status, 400)
548

    
549
        ap["homepage"] = "a.stri.ng"
550
        status, body = self.create(ap, h_owner)
551
        self.assertEqual(status, 400)
552

    
553
        ap["resources"] = {42: 42}
554
        status, body = self.create(ap, h_owner)
555
        self.assertEqual(status, 400)
556

    
557
        ap["resources"] = {"service1.resource11": {"member_capacity": 512}}
558
        status, body = self.create(ap, h_owner)
559
        self.assertEqual(status, 201)
560

    
561
        ap["name"] = "non_domain_name"
562
        status, body = self.create(ap, h_owner)
563
        self.assertEqual(status, 400)
564

    
565
        ap["name"] = "domain.name"
566
        ap.pop("max_members")
567
        status, body = self.create(ap, h_owner)
568
        self.assertEqual(status, 400)
569

    
570
        filters = {"filter": {"state": "nonex"}}
571
        req = {"body": json.dumps(filters)}
572
        r = client.get(reverse("api_projects"), req, **h_owner)
573
        self.assertEqual(r.status_code, 400)
574

    
575
        filters = {"filter": {"nonex": "nonex"}}
576
        req = {"body": json.dumps(filters)}
577
        r = client.get(reverse("api_projects"), req, **h_owner)
578
        self.assertEqual(r.status_code, 400)
579

    
580
        req = {"body": json.dumps({"project": "nonex"})}
581
        r = client.get(reverse("api_applications"), req, **h_owner)
582
        self.assertEqual(r.status_code, 400)
583

    
584
        req = {"body": json.dumps({"project": "nonex"})}
585
        r = client.get(reverse("api_memberships"), req, **h_owner)
586
        self.assertEqual(r.status_code, 400)
587

    
588

    
589
class TestProjects(TestCase):
590
    """
591
    Test projects.
592
    """
593
    def setUp(self):
594
        # astakos resources
595
        self.resource = Resource.objects.create(name="astakos.pending_app",
596
                                                uplimit=0,
597
                                                allow_in_projects=False,
598
                                                service_type="astakos")
599

    
600
        # custom service resources
601
        self.resource = Resource.objects.create(name="service1.resource",
602
                                                uplimit=100,
603
                                                service_type="service1")
604
        self.admin = get_local_user("projects-admin@synnefo.org")
605
        self.admin.uuid = 'uuid1'
606
        self.admin.save()
607

    
608
        self.user = get_local_user("user@synnefo.org")
609
        self.member = get_local_user("member@synnefo.org")
610
        self.member2 = get_local_user("member2@synnefo.org")
611

    
612
        self.admin_client = get_user_client("projects-admin@synnefo.org")
613
        self.user_client = get_user_client("user@synnefo.org")
614
        self.member_client = get_user_client("member@synnefo.org")
615
        self.member2_client = get_user_client("member2@synnefo.org")
616

    
617
        quotas.qh_sync_users(AstakosUser.objects.all())
618

    
619
    def tearDown(self):
620
        Service.objects.all().delete()
621
        ProjectApplication.objects.all().delete()
622
        Project.objects.all().delete()
623
        AstakosUser.objects.all().delete()
624

    
625
    @im_settings(PROJECT_ADMINS=['uuid1'])
626
    def test_application_limit(self):
627
        # user cannot create a project
628
        r = self.user_client.get(reverse('project_add'), follow=True)
629
        self.assertRedirects(r, reverse('project_list'))
630
        self.assertContains(r, "You are not allowed to create a new project")
631

    
632
        # but admin can
633
        r = self.admin_client.get(reverse('project_add'), follow=True)
634
        self.assertRedirects(r, reverse('project_add'))
635

    
636
    @im_settings(PROJECT_ADMINS=['uuid1'])
637
    def test_allow_in_project(self):
638
        dfrom = datetime.now()
639
        dto = datetime.now() + timedelta(days=30)
640

    
641
        # astakos.pending_uplimit allow_in_project flag is False
642
        # we shouldn't be able to create a project application using this
643
        # resource.
644
        application_data = {
645
            'name': 'project.synnefo.org',
646
            'homepage': 'https://www.synnefo.org',
647
            'start_date': dfrom.strftime("%Y-%m-%d"),
648
            'end_date': dto.strftime("%Y-%m-%d"),
649
            'member_join_policy': 2,
650
            'member_leave_policy': 1,
651
            'limit_on_members_number': 5,
652
            'service1.resource_uplimit': 100,
653
            'is_selected_service1.resource': "1",
654
            'astakos.pending_app_uplimit': 100,
655
            'is_selected_accounts': "1",
656
            'user': self.user.pk
657
        }
658
        form = forms.ProjectApplicationForm(data=application_data)
659
        # form is invalid
660
        self.assertEqual(form.is_valid(), False)
661

    
662
        del application_data['astakos.pending_app_uplimit']
663
        del application_data['is_selected_accounts']
664
        form = forms.ProjectApplicationForm(data=application_data)
665
        self.assertEqual(form.is_valid(), True)
666

    
667
    @im_settings(PROJECT_ADMINS=['uuid1'])
668
    def test_applications(self):
669
        # let user have 2 pending applications
670
        quotas.add_base_quota(self.user, 'astakos.pending_app', 2)
671

    
672
        r = self.user_client.get(reverse('project_add'), follow=True)
673
        self.assertRedirects(r, reverse('project_add'))
674

    
675
        # user fills the project application form
676
        post_url = reverse('project_add') + '?verify=1'
677
        dfrom = datetime.now()
678
        dto = datetime.now() + timedelta(days=30)
679
        application_data = {
680
            'name': 'project.synnefo.org',
681
            'homepage': 'https://www.synnefo.org',
682
            'start_date': dfrom.strftime("%Y-%m-%d"),
683
            'end_date': dto.strftime("%Y-%m-%d"),
684
            'member_join_policy': 2,
685
            'member_leave_policy': 1,
686
            'service1.resource_uplimit': 100,
687
            'is_selected_service1.resource': "1",
688
            'user': self.user.pk
689
        }
690
        r = self.user_client.post(post_url, data=application_data, follow=True)
691
        self.assertEqual(r.status_code, 200)
692
        self.assertEqual(r.context['form'].is_valid(), False)
693

    
694
        application_data['limit_on_members_number'] = 5
695
        r = self.user_client.post(post_url, data=application_data, follow=True)
696
        self.assertEqual(r.status_code, 200)
697
        self.assertEqual(r.context['form'].is_valid(), True)
698

    
699
        # confirm request
700
        post_url = reverse('project_add') + '?verify=0&edit=0'
701
        r = self.user_client.post(post_url, data=application_data, follow=True)
702
        self.assertContains(r, "The project application has been received")
703
        self.assertRedirects(r, reverse('project_list'))
704
        self.assertEqual(ProjectApplication.objects.count(), 1)
705
        app1 = ProjectApplication.objects.filter().order_by('pk')[0]
706
        app1_id = app1.pk
707
        project1_id = app1.chain_id
708

    
709
        # create another one
710
        application_data['name'] = 'project2.synnefo.org'
711
        r = self.user_client.post(post_url, data=application_data, follow=True)
712
        app2 = ProjectApplication.objects.filter().order_by('pk')[1]
713
        project2_id = app2.chain_id
714

    
715
        # no more applications (LIMIT is 2)
716
        r = self.user_client.get(reverse('project_add'), follow=True)
717
        self.assertRedirects(r, reverse('project_list'))
718
        self.assertContains(r, "You are not allowed to create a new project")
719

    
720
        # one project per application
721
        self.assertEqual(Project.objects.count(), 2)
722

    
723
        # login
724
        self.admin_client.get(reverse("edit_profile"))
725
        # admin approves
726
        r = self.admin_client.post(reverse('project_app_approve',
727
                                           kwargs={'application_id': app1_id}),
728
                                   follow=True)
729
        self.assertEqual(r.status_code, 200)
730

    
731
        Q_ACTIVE = Project.o_state_q(Project.O_ACTIVE)
732
        self.assertEqual(Project.objects.filter(Q_ACTIVE).count(), 1)
733

    
734
        # login
735
        self.member_client.get(reverse("edit_profile"))
736
        # cannot join project2 (not approved yet)
737
        join_url = reverse("project_join", kwargs={'chain_id': project2_id})
738
        r = self.member_client.post(join_url, follow=True)
739

    
740
        # can join project1
741
        self.member_client.get(reverse("edit_profile"))
742
        join_url = reverse("project_join", kwargs={'chain_id': project1_id})
743
        r = self.member_client.post(join_url, follow=True)
744
        self.assertEqual(r.status_code, 200)
745

    
746
        memberships = ProjectMembership.objects.all()
747
        self.assertEqual(len(memberships), 1)
748
        memb_id = memberships[0].id
749

    
750
        reject_member_url = reverse('project_reject_member',
751
                                    kwargs={'memb_id': memb_id})
752
        accept_member_url = reverse('project_accept_member',
753
                                    kwargs={'memb_id': memb_id})
754

    
755
        # only project owner is allowed to reject
756
        r = self.member_client.post(reject_member_url, follow=True)
757
        self.assertContains(r, "You do not have the permissions")
758
        self.assertEqual(r.status_code, 200)
759

    
760
        # user (owns project) rejects membership
761
        r = self.user_client.post(reject_member_url, follow=True)
762
        self.assertEqual(ProjectMembership.objects.any_accepted().count(), 0)
763

    
764
        # user rejoins
765
        self.member_client.get(reverse("edit_profile"))
766
        join_url = reverse("project_join", kwargs={'chain_id': app1_id})
767
        r = self.member_client.post(join_url, follow=True)
768
        self.assertEqual(r.status_code, 200)
769
        self.assertEqual(ProjectMembership.objects.requested().count(), 1)
770

    
771
        # user (owns project) accepts membership
772
        r = self.user_client.post(accept_member_url, follow=True)
773
        self.assertEqual(ProjectMembership.objects.any_accepted().count(), 1)
774
        membership = ProjectMembership.objects.get()
775
        self.assertEqual(membership.state, ProjectMembership.ACCEPTED)
776

    
777
        user_quotas = quotas.get_users_quotas([self.member])
778
        resource = 'service1.resource'
779
        newlimit = user_quotas[self.member.uuid]['system'][resource]['limit']
780
        # 100 from initial uplimit + 100 from project
781
        self.assertEqual(newlimit, 200)
782

    
783
        remove_member_url = reverse('project_remove_member',
784
                                    kwargs={'memb_id': membership.id})
785
        r = self.user_client.post(remove_member_url, follow=True)
786
        self.assertEqual(r.status_code, 200)
787

    
788
        user_quotas = quotas.get_users_quotas([self.member])
789
        resource = 'service1.resource'
790
        newlimit = user_quotas[self.member.uuid]['system'][resource]['limit']
791
        # 200 - 100 from project
792
        self.assertEqual(newlimit, 100)
793

    
794
        # support email gets rendered in emails content
795
        for mail in get_mailbox('user@synnefo.org'):
796
            self.assertTrue(settings.CONTACT_EMAIL in
797
                            mail.message().as_string())