github.com/decred/politeia@v1.4.0/politeiawww/cmd/pictl/testing.go (about)

     1  // Copyright (c) 2021 The Decred developers
     2  // Use of this source code is governed by an ISC
     3  // license that can be found in the LICENSE file.
     4  
     5  package main
     6  
     7  import (
     8  	"encoding/hex"
     9  	"fmt"
    10  	"strconv"
    11  
    12  	rcv1 "github.com/decred/politeia/politeiawww/api/records/v1"
    13  	"github.com/decred/politeia/politeiawww/cmd/shared"
    14  	"github.com/decred/politeia/util"
    15  )
    16  
    17  type user struct {
    18  	Email    string
    19  	Password string
    20  	Username string
    21  }
    22  
    23  // userNew creates a new user.
    24  //
    25  // This function returns with the user logged out.
    26  func userNew(email, username, password string) (*user, error) {
    27  	// Create user
    28  	c := userNewCmd{
    29  		Verify: true,
    30  	}
    31  	c.Args.Email = email
    32  	c.Args.Username = username
    33  	c.Args.Password = password
    34  	err := c.Execute(nil)
    35  	if err != nil {
    36  		return nil, fmt.Errorf("userNewCmd: %v", err)
    37  	}
    38  
    39  	// Log out user
    40  	err = userLogout()
    41  	if err != nil {
    42  		return nil, err
    43  	}
    44  
    45  	return &user{
    46  		Email:    email,
    47  		Username: username,
    48  		Password: password,
    49  	}, nil
    50  }
    51  
    52  // userNewRandom creates a new user with random credentials.
    53  //
    54  // This function returns with the user logged out.
    55  func userNewRandom() (*user, error) {
    56  	// Hex encoding creates 2x the number of characters as bytes.
    57  	// Ex: 4 bytes will results in a 8 character hex string.
    58  	b, err := util.Random(5)
    59  	if err != nil {
    60  		return nil, err
    61  	}
    62  	var (
    63  		r        = hex.EncodeToString(b)
    64  		email    = r + "@example.com"
    65  		username = "user_" + r
    66  		password = r
    67  	)
    68  	return userNew(email, username, password)
    69  }
    70  
    71  // userLogin logs in the provided user.
    72  func userLogin(u user) error {
    73  	c := shared.LoginCmd{}
    74  	c.Args.Email = u.Email
    75  	c.Args.Password = u.Password
    76  	err := c.Execute(nil)
    77  	if err != nil {
    78  		return fmt.Errorf("LoginCmd: %v", err)
    79  	}
    80  	return nil
    81  }
    82  
    83  // userLogout logs out any logged in user.
    84  func userLogout() error {
    85  	c := shared.LogoutCmd{}
    86  	err := c.Execute(nil)
    87  	if err != nil {
    88  		return fmt.Errorf("LogoutCmd: %v", err)
    89  	}
    90  	return nil
    91  }
    92  
    93  // proposalOpts includes all the possible configurations which can be used
    94  // when creating a new proposal using the cmdProposalNew command.
    95  type proposalOpts struct {
    96  	Name      string
    97  	LinkTo    string
    98  	LinkBy    string
    99  	Amount    uint64
   100  	StartDate string
   101  	EndDate   string
   102  	Domain    string
   103  
   104  	RFP bool
   105  
   106  	Random bool
   107  
   108  	RandomImages bool
   109  }
   110  
   111  // proposalUnreviewed creates a new proposal and leaves its status as
   112  // unreviewed.
   113  //
   114  // This function returns with the user logged out.
   115  func proposalUnreviewed(u user, opts *proposalOpts) (*rcv1.Record, error) {
   116  	// Login user
   117  	err := userLogin(u)
   118  	if err != nil {
   119  		return nil, err
   120  	}
   121  
   122  	// Submit new proposal
   123  	var cn cmdProposalNew
   124  	if opts != nil {
   125  		if opts.Name != "" {
   126  			cn.Name = opts.Name
   127  		}
   128  		if opts.LinkTo != "" {
   129  			cn.LinkTo = opts.LinkTo
   130  		}
   131  		if opts.LinkBy != "" {
   132  			cn.LinkBy = opts.LinkBy
   133  		}
   134  		if opts.Amount != 0 {
   135  			cn.Amount = opts.Amount
   136  		}
   137  		if opts.StartDate != "" {
   138  			cn.StartDate = opts.StartDate
   139  		}
   140  		if opts.EndDate != "" {
   141  			cn.EndDate = opts.EndDate
   142  		}
   143  		if opts.Domain != "" {
   144  			cn.Domain = opts.Domain
   145  		}
   146  		if opts.RFP {
   147  			cn.RFP = opts.RFP
   148  		}
   149  		if opts.Random {
   150  			cn.Random = opts.Random
   151  		}
   152  		if opts.RandomImages {
   153  			cn.RandomImages = opts.RandomImages
   154  		}
   155  	}
   156  	r, err := proposalNew(&cn)
   157  	if err != nil {
   158  		return nil, fmt.Errorf("cmdProposalNew: %v", err)
   159  	}
   160  
   161  	// Edit the proposal
   162  	r, err = proposalEditWithOpts(r.CensorshipRecord.Token, opts)
   163  	if err != nil {
   164  		return nil, err
   165  	}
   166  
   167  	// Logout user
   168  	err = userLogout()
   169  	if err != nil {
   170  		return nil, err
   171  	}
   172  
   173  	return r, nil
   174  }
   175  
   176  // proposalUnvettedCensored creates a new proposal then censors the proposal.
   177  //
   178  // This function returns with all users logged out.
   179  func proposalUnvettedCensored(author, admin user, opts *proposalOpts) (*rcv1.Record, error) {
   180  	// Setup an unvetted proposal
   181  	r, err := proposalUnreviewed(author, opts)
   182  	if err != nil {
   183  		return nil, err
   184  	}
   185  
   186  	// Login admin
   187  	err = userLogin(admin)
   188  	if err != nil {
   189  		return nil, err
   190  	}
   191  
   192  	// Censor the proposal
   193  	cs := cmdProposalSetStatus{}
   194  	cs.Args.Token = r.CensorshipRecord.Token
   195  	cs.Args.Status = strconv.Itoa(int(rcv1.RecordStatusCensored))
   196  	cs.Args.Reason = "Violates proposal rules."
   197  	cs.Args.Version = r.Version
   198  	r, err = proposalSetStatus(&cs)
   199  	if err != nil {
   200  		return nil, fmt.Errorf("cmdProposalSetStatus: %v", err)
   201  	}
   202  
   203  	// Logout admin
   204  	err = userLogout()
   205  	if err != nil {
   206  		return nil, err
   207  	}
   208  
   209  	return r, nil
   210  }
   211  
   212  // proposalPublic creates a new proposal then makes it public.
   213  //
   214  // This function returns with all users logged out.
   215  func proposalPublic(author, admin user, opts *proposalOpts) (*rcv1.Record, error) {
   216  	// Setup an unvetted proposal
   217  	r, err := proposalUnreviewed(author, opts)
   218  	if err != nil {
   219  		return nil, err
   220  	}
   221  
   222  	// Login admin
   223  	err = userLogin(admin)
   224  	if err != nil {
   225  		return nil, err
   226  	}
   227  
   228  	// Make the proposal public
   229  	cs := cmdProposalSetStatus{}
   230  	cs.Args.Token = r.CensorshipRecord.Token
   231  	cs.Args.Status = strconv.Itoa(int(rcv1.RecordStatusPublic))
   232  	cs.Args.Version = r.Version
   233  	r, err = proposalSetStatus(&cs)
   234  	if err != nil {
   235  		return nil, fmt.Errorf("cmdProposalSetStatus: %v", err)
   236  	}
   237  
   238  	// Logout admin
   239  	err = userLogout()
   240  	if err != nil {
   241  		return nil, err
   242  	}
   243  
   244  	// Login author
   245  	err = userLogin(author)
   246  	if err != nil {
   247  		return nil, err
   248  	}
   249  
   250  	// Edit the proposal
   251  	r, err = proposalEditWithOpts(r.CensorshipRecord.Token, opts)
   252  	if err != nil {
   253  		return nil, err
   254  	}
   255  
   256  	// Logout author
   257  	err = userLogout()
   258  	if err != nil {
   259  		return nil, err
   260  	}
   261  
   262  	return r, nil
   263  }
   264  
   265  // proposalEditWithOpts edits a proposal, in addition to the proposal tokens, it
   266  // accepts a pointer to a proposalOpts struct which can be used to configure
   267  // the various arguments and flags of the cmdProposalEdit command.
   268  func proposalEditWithOpts(token string, opts *proposalOpts) (*rcv1.Record, error) {
   269  	var ce cmdProposalEdit
   270  	if opts != nil {
   271  		if opts.Name != "" {
   272  			ce.Name = opts.Name
   273  		}
   274  		if opts.LinkTo != "" {
   275  			ce.LinkTo = opts.LinkTo
   276  		}
   277  		if opts.LinkBy != "" {
   278  			ce.LinkBy = opts.LinkBy
   279  		}
   280  		if opts.Amount != 0 {
   281  			ce.Amount = opts.Amount
   282  		}
   283  		if opts.StartDate != "" {
   284  			ce.StartDate = opts.StartDate
   285  		}
   286  		if opts.EndDate != "" {
   287  			ce.EndDate = opts.EndDate
   288  		}
   289  		if opts.Domain != "" {
   290  			ce.Domain = opts.Domain
   291  		}
   292  		if opts.RFP {
   293  			ce.RFP = opts.RFP
   294  		}
   295  		if opts.Random {
   296  			ce.Random = opts.Random
   297  		}
   298  		if opts.RandomImages {
   299  			ce.RandomImages = opts.RandomImages
   300  		}
   301  	}
   302  	ce.Args.Token = token
   303  	r, err := proposalEdit(&ce)
   304  	if err != nil {
   305  		return nil, fmt.Errorf("cmdProposalEdit: %v", err)
   306  	}
   307  
   308  	return r, nil
   309  }
   310  
   311  // proposalVettedCensored creates a new proposal, makes the proposal public,
   312  // then censors the proposal.
   313  //
   314  // This function returns with all users logged out.
   315  func proposalVettedCensored(author, admin user, opts *proposalOpts) (*rcv1.Record, error) {
   316  	// Create a public proposal
   317  	r, err := proposalPublic(author, admin, opts)
   318  	if err != nil {
   319  		return nil, err
   320  	}
   321  
   322  	// Login admin
   323  	err = userLogin(admin)
   324  	if err != nil {
   325  		return nil, err
   326  	}
   327  
   328  	// Censor the proposal
   329  	cs := cmdProposalSetStatus{}
   330  	cs.Args.Token = r.CensorshipRecord.Token
   331  	cs.Args.Status = strconv.Itoa(int(rcv1.RecordStatusCensored))
   332  	cs.Args.Reason = "Violates proposal rules."
   333  	cs.Args.Version = r.Version
   334  	r, err = proposalSetStatus(&cs)
   335  	if err != nil {
   336  		return nil, fmt.Errorf("cmdProposalSetStatus: %v", err)
   337  	}
   338  
   339  	// Logout admin
   340  	err = userLogout()
   341  	if err != nil {
   342  		return nil, err
   343  	}
   344  
   345  	return r, nil
   346  }
   347  
   348  // proposalAbandoned creates a new proposal, makes the proposal public,
   349  // then abandones the proposal.
   350  //
   351  // This function returns with all users logged out.
   352  func proposalAbandoned(author, admin user, opts *proposalOpts) (*rcv1.Record, error) {
   353  	// Create a public proposal
   354  	r, err := proposalPublic(author, admin, opts)
   355  	if err != nil {
   356  		return nil, err
   357  	}
   358  
   359  	// Login admin
   360  	err = userLogin(admin)
   361  	if err != nil {
   362  		return nil, err
   363  	}
   364  
   365  	// Abandone the proposal
   366  	cs := cmdProposalSetStatus{}
   367  	cs.Args.Token = r.CensorshipRecord.Token
   368  	cs.Args.Status = strconv.Itoa(int(rcv1.RecordStatusArchived))
   369  	cs.Args.Reason = "No activity from author in 3 weeks."
   370  	cs.Args.Version = r.Version
   371  	r, err = proposalSetStatus(&cs)
   372  	if err != nil {
   373  		return nil, fmt.Errorf("cmdProposalSetStatus: %v", err)
   374  	}
   375  
   376  	// Logout admin
   377  	err = userLogout()
   378  	if err != nil {
   379  		return nil, err
   380  	}
   381  
   382  	return r, nil
   383  }
   384  
   385  // voteAuthorize authorizes the ticket vote.
   386  //
   387  // This function returns with the user logged out.
   388  func voteAuthorize(author user, token string) error {
   389  	// Login author
   390  	err := userLogin(author)
   391  	if err != nil {
   392  		return err
   393  	}
   394  
   395  	// Authorize the voting period
   396  	c := cmdVoteAuthorize{}
   397  	c.Args.Token = token
   398  	err = c.Execute(nil)
   399  	if err != nil {
   400  		return fmt.Errorf("cmdVoteAuthorize: %v", err)
   401  	}
   402  
   403  	// Logout author
   404  	err = userLogout()
   405  	if err != nil {
   406  		return err
   407  	}
   408  
   409  	return nil
   410  }
   411  
   412  // voteStart starts the voting period on a record. The runoff param can be
   413  // used to start a runoff vote on a RFP.
   414  //
   415  // This function returns with the admin logged out.
   416  func voteStart(admin user, token string, duration, quorum, pass uint32, runoff bool) error {
   417  	// Login admin
   418  	err := userLogin(admin)
   419  	if err != nil {
   420  		return err
   421  	}
   422  
   423  	// Start the voting period
   424  	c := cmdVoteStart{
   425  		Runoff:   runoff,
   426  		Duration: duration,
   427  		Quorum:   &quorum,
   428  		Passing:  pass,
   429  	}
   430  	c.Args.Token = token
   431  	err = c.Execute(nil)
   432  	if err != nil {
   433  		return fmt.Errorf("cmdVoteStart: %v", err)
   434  	}
   435  
   436  	// Logout admin
   437  	err = userLogout()
   438  	if err != nil {
   439  		return err
   440  	}
   441  
   442  	return nil
   443  }