Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (29.9 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
                "resources": {"service1.resource11": {
166
                    "member_capacity": 512}}
167
                }
168

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
560
        filters = {"filter": {"state": "nonex"}}
561
        req = {"body": json.dumps(filters)}
562
        r = client.get(reverse("api_projects"), req, **h_owner)
563
        self.assertEqual(r.status_code, 400)
564

    
565
        filters = {"filter": {"nonex": "nonex"}}
566
        req = {"body": json.dumps(filters)}
567
        r = client.get(reverse("api_projects"), req, **h_owner)
568
        self.assertEqual(r.status_code, 400)
569

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

    
574
        req = {"body": json.dumps({"project": "nonex"})}
575
        r = client.get(reverse("api_memberships"), req, **h_owner)
576
        self.assertEqual(r.status_code, 400)
577

    
578

    
579
class TestProjects(TestCase):
580
    """
581
    Test projects.
582
    """
583
    def setUp(self):
584
        # astakos resources
585
        self.resource = Resource.objects.create(name="astakos.pending_app",
586
                                                uplimit=0,
587
                                                allow_in_projects=False,
588
                                                service_type="astakos")
589

    
590
        # custom service resources
591
        self.resource = Resource.objects.create(name="service1.resource",
592
                                                uplimit=100,
593
                                                service_type="service1")
594
        self.admin = get_local_user("projects-admin@synnefo.org")
595
        self.admin.uuid = 'uuid1'
596
        self.admin.save()
597

    
598
        self.user = get_local_user("user@synnefo.org")
599
        self.member = get_local_user("member@synnefo.org")
600
        self.member2 = get_local_user("member2@synnefo.org")
601

    
602
        self.admin_client = get_user_client("projects-admin@synnefo.org")
603
        self.user_client = get_user_client("user@synnefo.org")
604
        self.member_client = get_user_client("member@synnefo.org")
605
        self.member2_client = get_user_client("member2@synnefo.org")
606

    
607
        quotas.qh_sync_users(AstakosUser.objects.all())
608

    
609
    def tearDown(self):
610
        Service.objects.all().delete()
611
        ProjectApplication.objects.all().delete()
612
        Project.objects.all().delete()
613
        AstakosUser.objects.all().delete()
614

    
615
    @im_settings(PROJECT_ADMINS=['uuid1'])
616
    def test_application_limit(self):
617
        # user cannot create a project
618
        r = self.user_client.get(reverse('project_add'), follow=True)
619
        self.assertRedirects(r, reverse('project_list'))
620
        self.assertContains(r, "You are not allowed to create a new project")
621

    
622
        # but admin can
623
        r = self.admin_client.get(reverse('project_add'), follow=True)
624
        self.assertRedirects(r, reverse('project_add'))
625

    
626
    @im_settings(PROJECT_ADMINS=['uuid1'])
627
    def test_allow_in_project(self):
628
        dfrom = datetime.now()
629
        dto = datetime.now() + timedelta(days=30)
630

    
631
        # astakos.pending_uplimit allow_in_project flag is False
632
        # we shouldn't be able to create a project application using this
633
        # resource.
634
        application_data = {
635
            'name': 'project.synnefo.org',
636
            'homepage': 'https://www.synnefo.org',
637
            'start_date': dfrom.strftime("%Y-%m-%d"),
638
            'end_date': dto.strftime("%Y-%m-%d"),
639
            'member_join_policy': 2,
640
            'member_leave_policy': 1,
641
            'service1.resource_uplimit': 100,
642
            'is_selected_service1.resource': "1",
643
            'astakos.pending_app_uplimit': 100,
644
            'is_selected_accounts': "1",
645
            'user': self.user.pk
646
        }
647
        form = forms.ProjectApplicationForm(data=application_data)
648
        # form is invalid
649
        self.assertEqual(form.is_valid(), False)
650

    
651
        del application_data['astakos.pending_app_uplimit']
652
        del application_data['is_selected_accounts']
653
        form = forms.ProjectApplicationForm(data=application_data)
654
        self.assertEqual(form.is_valid(), True)
655

    
656
    @im_settings(PROJECT_ADMINS=['uuid1'])
657
    def test_applications(self):
658
        # let user have 2 pending applications
659
        quotas.add_base_quota(self.user, 'astakos.pending_app', 2)
660

    
661
        r = self.user_client.get(reverse('project_add'), follow=True)
662
        self.assertRedirects(r, reverse('project_add'))
663

    
664
        # user fills the project application form
665
        post_url = reverse('project_add') + '?verify=1'
666
        dfrom = datetime.now()
667
        dto = datetime.now() + timedelta(days=30)
668
        application_data = {
669
            'name': 'project.synnefo.org',
670
            'homepage': 'https://www.synnefo.org',
671
            'start_date': dfrom.strftime("%Y-%m-%d"),
672
            'end_date': dto.strftime("%Y-%m-%d"),
673
            'member_join_policy': 2,
674
            'member_leave_policy': 1,
675
            'service1.resource_uplimit': 100,
676
            'is_selected_service1.resource': "1",
677
            'user': self.user.pk
678
        }
679
        r = self.user_client.post(post_url, data=application_data, follow=True)
680
        self.assertEqual(r.status_code, 200)
681
        self.assertEqual(r.context['form'].is_valid(), True)
682

    
683
        # confirm request
684
        post_url = reverse('project_add') + '?verify=0&edit=0'
685
        r = self.user_client.post(post_url, data=application_data, follow=True)
686
        self.assertContains(r, "The project application has been received")
687
        self.assertRedirects(r, reverse('project_list'))
688
        self.assertEqual(ProjectApplication.objects.count(), 1)
689
        app1 = ProjectApplication.objects.filter().order_by('pk')[0]
690
        app1_id = app1.pk
691
        project1_id = app1.chain_id
692

    
693
        # create another one
694
        application_data['name'] = 'project2.synnefo.org'
695
        r = self.user_client.post(post_url, data=application_data, follow=True)
696
        app2 = ProjectApplication.objects.filter().order_by('pk')[1]
697
        project2_id = app2.chain_id
698

    
699
        # no more applications (LIMIT is 2)
700
        r = self.user_client.get(reverse('project_add'), follow=True)
701
        self.assertRedirects(r, reverse('project_list'))
702
        self.assertContains(r, "You are not allowed to create a new project")
703

    
704
        # one project per application
705
        self.assertEqual(Project.objects.count(), 2)
706

    
707
        # login
708
        self.admin_client.get(reverse("edit_profile"))
709
        # admin approves
710
        r = self.admin_client.post(reverse('project_app_approve',
711
                                           kwargs={'application_id': app1_id}),
712
                                   follow=True)
713
        self.assertEqual(r.status_code, 200)
714

    
715
        Q_ACTIVE = Project.o_state_q(Project.O_ACTIVE)
716
        self.assertEqual(Project.objects.filter(Q_ACTIVE).count(), 1)
717

    
718
        # login
719
        self.member_client.get(reverse("edit_profile"))
720
        # cannot join project2 (not approved yet)
721
        join_url = reverse("project_join", kwargs={'chain_id': project2_id})
722
        r = self.member_client.post(join_url, follow=True)
723

    
724
        # can join project1
725
        self.member_client.get(reverse("edit_profile"))
726
        join_url = reverse("project_join", kwargs={'chain_id': project1_id})
727
        r = self.member_client.post(join_url, follow=True)
728
        self.assertEqual(r.status_code, 200)
729

    
730
        memberships = ProjectMembership.objects.all()
731
        self.assertEqual(len(memberships), 1)
732
        memb_id = memberships[0].id
733

    
734
        reject_member_url = reverse('project_reject_member',
735
                                    kwargs={'memb_id': memb_id})
736
        accept_member_url = reverse('project_accept_member',
737
                                    kwargs={'memb_id': memb_id})
738

    
739
        # only project owner is allowed to reject
740
        r = self.member_client.post(reject_member_url, follow=True)
741
        self.assertContains(r, "You do not have the permissions")
742
        self.assertEqual(r.status_code, 200)
743

    
744
        # user (owns project) rejects membership
745
        r = self.user_client.post(reject_member_url, follow=True)
746
        self.assertEqual(ProjectMembership.objects.any_accepted().count(), 0)
747

    
748
        # user rejoins
749
        self.member_client.get(reverse("edit_profile"))
750
        join_url = reverse("project_join", kwargs={'chain_id': app1_id})
751
        r = self.member_client.post(join_url, follow=True)
752
        self.assertEqual(r.status_code, 200)
753
        self.assertEqual(ProjectMembership.objects.requested().count(), 1)
754

    
755
        # user (owns project) accepts membership
756
        r = self.user_client.post(accept_member_url, follow=True)
757
        self.assertEqual(ProjectMembership.objects.any_accepted().count(), 1)
758
        membership = ProjectMembership.objects.get()
759
        self.assertEqual(membership.state, ProjectMembership.ACCEPTED)
760

    
761
        user_quotas = quotas.get_users_quotas([self.member])
762
        resource = 'service1.resource'
763
        newlimit = user_quotas[self.member.uuid]['system'][resource]['limit']
764
        # 100 from initial uplimit + 100 from project
765
        self.assertEqual(newlimit, 200)
766

    
767
        remove_member_url = reverse('project_remove_member',
768
                                    kwargs={'memb_id': membership.id})
769
        r = self.user_client.post(remove_member_url, follow=True)
770
        self.assertEqual(r.status_code, 200)
771

    
772
        user_quotas = quotas.get_users_quotas([self.member])
773
        resource = 'service1.resource'
774
        newlimit = user_quotas[self.member.uuid]['system'][resource]['limit']
775
        # 200 - 100 from project
776
        self.assertEqual(newlimit, 100)
777

    
778
        # support email gets rendered in emails content
779
        for mail in get_mailbox('user@synnefo.org'):
780
            self.assertTrue(settings.CONTACT_EMAIL in
781
                            mail.message().as_string())