Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (30.8 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
        # Enroll fails, project does not exist
356
        status, body = self.enroll(-1, self.user1, h_owner)
357
        self.assertEqual(status, 409)
358

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

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

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

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

    
389
        # Leave failed
390
        status = self.memb_action(m_plain_id, "leave", h_owner)
391
        self.assertEqual(status, 403)
392

    
393
        # Leave
394
        status = self.memb_action(m_plain_id, "leave", h_plain)
395
        self.assertEqual(status, 200)
396

    
397
        # Suspend failed
398
        status = self.project_action(project_id, "suspend", h_owner)
399
        self.assertEqual(status, 403)
400

    
401
        # Unsuspend failed
402
        status = self.project_action(project_id, "unsuspend", h_admin)
403
        self.assertEqual(status, 409)
404

    
405
        # Suspend
406
        status = self.project_action(project_id, "suspend", h_admin)
407
        self.assertEqual(status, 200)
408

    
409
        # Cannot view project
410
        r = client.get(reverse("api_project",
411
                               kwargs={"project_id": project_id}), **h_plain)
412
        self.assertEqual(r.status_code, 403)
413

    
414
        # Unsuspend
415
        status = self.project_action(project_id, "unsuspend", h_admin)
416
        self.assertEqual(status, 200)
417

    
418
        # Cannot approve, project with same name exists
419
        status = self.app_action(project2_app_id, "approve", h_admin)
420
        self.assertEqual(status, 409)
421

    
422
        # Terminate
423
        status = self.project_action(project_id, "terminate", h_admin)
424
        self.assertEqual(status, 200)
425

    
426
        # Join failed
427
        status, _ = self.join(project_id, h_admin)
428
        self.assertEqual(status, 409)
429

    
430
        # Can approve now
431
        status = self.app_action(project2_app_id, "approve", h_admin)
432
        self.assertEqual(status, 200)
433

    
434
        # Join new project
435
        status, body = self.join(project2_id, h_plain)
436
        self.assertEqual(status, 200)
437
        m_project2 = body["id"]
438

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

    
446
        # Remove member
447
        status = self.memb_action(m_project2, "remove", h_owner)
448
        self.assertEqual(status, 200)
449

    
450
        # Reinstate failed
451
        status = self.project_action(project_id, "reinstate", h_admin)
452
        self.assertEqual(status, 409)
453

    
454
        # Rename
455
        app2_renamed = copy.deepcopy(app2)
456
        app2_renamed["name"] = "new.name"
457
        status, body = self.modify(app2_renamed, project_id, h_owner)
458
        self.assertEqual(status, 201)
459
        app2_renamed_id = body["application"]
460

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

    
470
        # Get application
471
        r = client.get(reverse("api_application",
472
                               kwargs={"app_id": app2_renamed_id}), **h_plain)
473
        self.assertEqual(r.status_code, 403)
474

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

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

    
489
        # Bad requests
490
        r = client.head(reverse("api_projects"), **h_admin)
491
        self.assertEqual(r.status_code, 405)
492
        self.assertTrue('Allow' in r)
493

    
494
        r = client.head(reverse("api_project",
495
                                kwargs={"project_id": 1}), **h_admin)
496
        self.assertEqual(r.status_code, 405)
497
        self.assertTrue('Allow' in r)
498

    
499
        r = client.head(reverse("api_applications"), **h_admin)
500
        self.assertEqual(r.status_code, 405)
501
        self.assertTrue('Allow' in r)
502

    
503
        r = client.head(reverse("api_memberships"), **h_admin)
504
        self.assertEqual(r.status_code, 405)
505
        self.assertTrue('Allow' in r)
506

    
507
        status = self.project_action(1, "nonex", h_owner)
508
        self.assertEqual(status, 400)
509

    
510
        action = json.dumps({"suspend": "", "unsuspend": ""})
511
        r = client.post(reverse("api_project_action",
512
                                kwargs={"project_id": 1}),
513
                        action, content_type="application/json", **h_owner)
514
        self.assertEqual(r.status_code, 400)
515

    
516
        ap = {"owner": "nonex",
517
              "join_policy": "nonex",
518
              "leave_policy": "nonex",
519
              "start_date": "nonex",
520
              "homepage": {},
521
              "max_members": -3,
522
              "resources": [],
523
              }
524

    
525
        status, body = self.create(ap, h_owner)
526
        self.assertEqual(status, 400)
527
        self.assertEqual(body["badRequest"]["message"], "User does not exist.")
528

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

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

    
537
        ap["join_policy"] = "auto"
538
        status, body = self.create(ap, h_owner)
539
        self.assertEqual(status, 400)
540

    
541
        ap["leave_policy"] = "closed"
542
        status, body = self.create(ap, h_owner)
543
        self.assertEqual(status, 400)
544

    
545
        ap["start_date"] = "2013-01-01T0:0Z"
546
        status, body = self.create(ap, h_owner)
547
        self.assertEqual(status, 400)
548

    
549
        ap["end_date"] = "2014-01-01T0:0Z"
550
        status, body = self.create(ap, h_owner)
551
        self.assertEqual(status, 400)
552

    
553
        ap["max_members"] = 0
554
        status, body = self.create(ap, h_owner)
555
        self.assertEqual(status, 400)
556

    
557
        ap["homepage"] = "a.stri.ng"
558
        status, body = self.create(ap, h_owner)
559
        self.assertEqual(status, 400)
560

    
561
        ap["resources"] = {42: 42}
562
        status, body = self.create(ap, h_owner)
563
        self.assertEqual(status, 400)
564

    
565
        ap["resources"] = {"service1.resource11": {"member_capacity": 512}}
566
        status, body = self.create(ap, h_owner)
567
        self.assertEqual(status, 201)
568

    
569
        ap["name"] = "non_domain_name"
570
        status, body = self.create(ap, h_owner)
571
        self.assertEqual(status, 400)
572

    
573
        ap["name"] = "domain.name"
574
        ap.pop("max_members")
575
        status, body = self.create(ap, h_owner)
576
        self.assertEqual(status, 400)
577

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

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

    
588
        req = {"body": json.dumps({"project": "nonex"})}
589
        r = client.get(reverse("api_applications"), req, **h_owner)
590
        self.assertEqual(r.status_code, 400)
591

    
592
        req = {"body": json.dumps({"project": "nonex"})}
593
        r = client.get(reverse("api_memberships"), req, **h_owner)
594
        self.assertEqual(r.status_code, 400)
595

    
596

    
597
class TestProjects(TestCase):
598
    """
599
    Test projects.
600
    """
601
    def setUp(self):
602
        # astakos resources
603
        self.resource = Resource.objects.create(name="astakos.pending_app",
604
                                                uplimit=0,
605
                                                allow_in_projects=False,
606
                                                service_type="astakos")
607

    
608
        # custom service resources
609
        self.resource = Resource.objects.create(name="service1.resource",
610
                                                uplimit=100,
611
                                                service_type="service1")
612
        self.admin = get_local_user("projects-admin@synnefo.org")
613
        self.admin.uuid = 'uuid1'
614
        self.admin.save()
615

    
616
        self.user = get_local_user("user@synnefo.org")
617
        self.member = get_local_user("member@synnefo.org")
618
        self.member2 = get_local_user("member2@synnefo.org")
619

    
620
        self.admin_client = get_user_client("projects-admin@synnefo.org")
621
        self.user_client = get_user_client("user@synnefo.org")
622
        self.member_client = get_user_client("member@synnefo.org")
623
        self.member2_client = get_user_client("member2@synnefo.org")
624

    
625
        quotas.qh_sync_users(AstakosUser.objects.all())
626

    
627
    def tearDown(self):
628
        Service.objects.all().delete()
629
        ProjectApplication.objects.all().delete()
630
        Project.objects.all().delete()
631
        AstakosUser.objects.all().delete()
632

    
633
    @im_settings(PROJECT_ADMINS=['uuid1'])
634
    def test_application_limit(self):
635
        # user cannot create a project
636
        r = self.user_client.get(reverse('project_add'), follow=True)
637
        self.assertRedirects(r, reverse('project_list'))
638
        self.assertContains(r, "You are not allowed to create a new project")
639

    
640
        # but admin can
641
        r = self.admin_client.get(reverse('project_add'), follow=True)
642
        self.assertRedirects(r, reverse('project_add'))
643

    
644
    @im_settings(PROJECT_ADMINS=['uuid1'])
645
    def test_allow_in_project(self):
646
        dfrom = datetime.now()
647
        dto = datetime.now() + timedelta(days=30)
648

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

    
670
        del application_data['astakos.pending_app_uplimit']
671
        del application_data['is_selected_accounts']
672
        form = forms.ProjectApplicationForm(data=application_data)
673
        self.assertEqual(form.is_valid(), True)
674

    
675
    @im_settings(PROJECT_ADMINS=['uuid1'])
676
    def test_applications(self):
677
        # let user have 2 pending applications
678
        quotas.add_base_quota(self.user, 'astakos.pending_app', 2)
679

    
680
        r = self.user_client.get(reverse('project_add'), follow=True)
681
        self.assertRedirects(r, reverse('project_add'))
682

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

    
702
        application_data['limit_on_members_number'] = 5
703
        r = self.user_client.post(post_url, data=application_data, follow=True)
704
        self.assertEqual(r.status_code, 200)
705
        self.assertEqual(r.context['form'].is_valid(), True)
706

    
707
        # confirm request
708
        post_url = reverse('project_add') + '?verify=0&edit=0'
709
        r = self.user_client.post(post_url, data=application_data, follow=True)
710
        self.assertContains(r, "The project application has been received")
711
        self.assertRedirects(r, reverse('project_list'))
712
        self.assertEqual(ProjectApplication.objects.count(), 1)
713
        app1 = ProjectApplication.objects.filter().order_by('pk')[0]
714
        app1_id = app1.pk
715
        project1_id = app1.chain_id
716

    
717
        # create another one
718
        application_data['name'] = 'project2.synnefo.org'
719
        r = self.user_client.post(post_url, data=application_data, follow=True)
720
        app2 = ProjectApplication.objects.filter().order_by('pk')[1]
721
        project2_id = app2.chain_id
722

    
723
        # no more applications (LIMIT is 2)
724
        r = self.user_client.get(reverse('project_add'), follow=True)
725
        self.assertRedirects(r, reverse('project_list'))
726
        self.assertContains(r, "You are not allowed to create a new project")
727

    
728
        # one project per application
729
        self.assertEqual(Project.objects.count(), 2)
730

    
731
        # login
732
        self.admin_client.get(reverse("edit_profile"))
733
        # admin approves
734
        r = self.admin_client.post(reverse('project_app_approve',
735
                                           kwargs={'application_id': app1_id}),
736
                                   follow=True)
737
        self.assertEqual(r.status_code, 200)
738

    
739
        Q_ACTIVE = Project.o_state_q(Project.O_ACTIVE)
740
        self.assertEqual(Project.objects.filter(Q_ACTIVE).count(), 1)
741

    
742
        # login
743
        self.member_client.get(reverse("edit_profile"))
744
        # cannot join project2 (not approved yet)
745
        join_url = reverse("project_join", kwargs={'chain_id': project2_id})
746
        r = self.member_client.post(join_url, follow=True)
747

    
748
        # can join project1
749
        self.member_client.get(reverse("edit_profile"))
750
        join_url = reverse("project_join", kwargs={'chain_id': project1_id})
751
        r = self.member_client.post(join_url, follow=True)
752
        self.assertEqual(r.status_code, 200)
753

    
754
        memberships = ProjectMembership.objects.all()
755
        self.assertEqual(len(memberships), 1)
756
        memb_id = memberships[0].id
757

    
758
        reject_member_url = reverse('project_reject_member',
759
                                    kwargs={'memb_id': memb_id})
760
        accept_member_url = reverse('project_accept_member',
761
                                    kwargs={'memb_id': memb_id})
762

    
763
        # only project owner is allowed to reject
764
        r = self.member_client.post(reject_member_url, follow=True)
765
        self.assertContains(r, "You do not have the permissions")
766
        self.assertEqual(r.status_code, 200)
767

    
768
        # user (owns project) rejects membership
769
        r = self.user_client.post(reject_member_url, follow=True)
770
        self.assertEqual(ProjectMembership.objects.any_accepted().count(), 0)
771

    
772
        # user rejoins
773
        self.member_client.get(reverse("edit_profile"))
774
        join_url = reverse("project_join", kwargs={'chain_id': project1_id})
775
        r = self.member_client.post(join_url, follow=True)
776
        self.assertEqual(r.status_code, 200)
777
        self.assertEqual(ProjectMembership.objects.requested().count(), 1)
778

    
779
        # user (owns project) accepts membership
780
        r = self.user_client.post(accept_member_url, follow=True)
781
        self.assertEqual(ProjectMembership.objects.any_accepted().count(), 1)
782
        membership = ProjectMembership.objects.get()
783
        self.assertEqual(membership.state, ProjectMembership.ACCEPTED)
784

    
785
        user_quotas = quotas.get_users_quotas([self.member])
786
        resource = 'service1.resource'
787
        newlimit = user_quotas[self.member.uuid]['system'][resource]['limit']
788
        # 100 from initial uplimit + 100 from project
789
        self.assertEqual(newlimit, 200)
790

    
791
        remove_member_url = reverse('project_remove_member',
792
                                    kwargs={'memb_id': membership.id})
793
        r = self.user_client.post(remove_member_url, follow=True)
794
        self.assertEqual(r.status_code, 200)
795

    
796
        user_quotas = quotas.get_users_quotas([self.member])
797
        resource = 'service1.resource'
798
        newlimit = user_quotas[self.member.uuid]['system'][resource]['limit']
799
        # 200 - 100 from project
800
        self.assertEqual(newlimit, 100)
801

    
802
        # support email gets rendered in emails content
803
        for mail in get_mailbox('user@synnefo.org'):
804
            self.assertTrue(settings.CONTACT_EMAIL in
805
                            mail.message().as_string())